Golang Capturing log.Println And fmt.Println Output

Hau Ma
Hau Ma
Jun 26, 2018 · 2 min read

Actually it’s about os.Stdout and os.Stderr :D

Have you ever get into a situation when writing unit test that need to assert the output of function? Well when you encounter it, you’ll in trouble. Today I’ll tell you about how to capture output from os.Stdout and os.Stderr which are primarily used by standard log utilities like: log.Println, fmt.Println

OS Pipeline

When you take a closer look at os.Stdout and os.Stderr, they have type *os.File so Golang support us a file pipeline method for read continuously from the reader which is piped from the writer which have data written to. I’ve published a blog explaining the io Pipeline so you can have deep understand about pipeline.

It’s time to dive into code, to init a os file pipeline we use the Go function os.Pipe

Simple, right? Now we created a pipe between reader and writer which have type *os.File, what come in to writer will come out from reader, remember that.

We need a io pipeline which have type *os.File because we want to replace standard os.Stdout and os.Stderr, which have type *os.File also, by this. So we can actually write and read the output from the program, that’s the point.

Capturing Program Output

First we’ll replace the standard os output with pipeline:

That’s it, all we need to do is replace global variables os.Stdout and os.Stderr with new writer and set the output for log package, log package default uses os.Stderr, but it just copy the address of os.Stderr, so if we change the address of os.Stderr itself, the output address in log package remains no change, luckily log package provide a method calls SetOutput to change the address of output object, so we can use it.

Second we’ll read the output and save it:

Because read and write can not stay in a same goroutine so we fork a new goroutine for read and save all the output into a buffer and send the output string to channel for main goroutine get it

Finally we’ll call the function we want to capture the ouput. Full working code is shown below:

Expert Note: because os.Stdout and os.Stderr is global variable of os package, when we temporary change it address to the pipeline to capture output, we need to set it back to origin when captureOutput returned

This program is not thread safe. How do we make it thread safe? Please do it as an exercise by yourself. Have fun!

Hau Ma

Written by

Hau Ma

Gather together the things that change for the same reasons. Separate those things that change for different reasons.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade