Logrus: while testing, redirect output to testing.T

Created on 4 Oct 2018  Â·  3Comments  Â·  Source: sirupsen/logrus

The testing package has many logging functions that are used for logging only when errors are encountered. It would be beneficial to be able to easily redirect output to this. I can't see a simple way to do this currently besides creating an io.Writer that outputs to testing.Logf.

question

Most helpful comment

@drewwells it's slightly tricky since you need to get access to the *testing.T instance of each test, and it is hardcoded to look back a certain number of stack frames for the calling location.

In one of our projects at work we have this:

package serenity

import (
    "io"
    "os"
    "testing"
    "github.com/sirupsen/logrus"
)

// LogCapturer reroutes testing.T log output
type LogCapturer interface {
    Release()
}

type logCapturer struct {
    *testing.T
    origOut io.Writer
}

func (tl logCapturer) Write(p []byte) (n int, err error) {
    tl.Logf((string)(p))
    return len(p), nil
}

func (tl logCapturer) Release() {
    logrus.SetOutput(tl.origOut)
}

// CaptureLog redirects logrus output to testing.Log
func CaptureLog(t *testing.T) LogCapturer {
    lc := logCapturer{T: t, origOut: logrus.StandardLogger().Out}
    if !testing.Verbose() {
        logrus.SetOutput(lc)
    }
    return &lc
}

But it does mean in every test you need to do:
```golang
func TestFoo(t *testing.T) {
defer serenity.CaptureLog(t).Release()
…
}

All 3 comments

Hi @drewwells, I've you tried somehow to set the logger.Out field to os.Stdout instead of os.Stderr ? I wonder if the test environment doesn't do something Stdout in order to only have its output upon test failure.

@drewwells it's slightly tricky since you need to get access to the *testing.T instance of each test, and it is hardcoded to look back a certain number of stack frames for the calling location.

In one of our projects at work we have this:

package serenity

import (
    "io"
    "os"
    "testing"
    "github.com/sirupsen/logrus"
)

// LogCapturer reroutes testing.T log output
type LogCapturer interface {
    Release()
}

type logCapturer struct {
    *testing.T
    origOut io.Writer
}

func (tl logCapturer) Write(p []byte) (n int, err error) {
    tl.Logf((string)(p))
    return len(p), nil
}

func (tl logCapturer) Release() {
    logrus.SetOutput(tl.origOut)
}

// CaptureLog redirects logrus output to testing.Log
func CaptureLog(t *testing.T) LogCapturer {
    lc := logCapturer{T: t, origOut: logrus.StandardLogger().Out}
    if !testing.Verbose() {
        logrus.SetOutput(lc)
    }
    return &lc
}

But it does mean in every test you need to do:
```golang
func TestFoo(t *testing.T) {
defer serenity.CaptureLog(t).Release()
…
}

We can use logrus.SetOutput(ioutil.Discard) to disable output.

import (
    "io/ioutil"
        "testing"
    "github.com/sirupsen/logrus"
)

func TestFoo(t *testing.T) {
    logrus.Info("Logrus in Testing")
}

func TestMain(m *testing.M) {
    logrus.SetOutput(ioutil.Discard)
    os.Exit(m.Run())
}

Note: we need to call m.Run() to execute the test after defining the TestMain.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Blogoslov picture Blogoslov  Â·  6Comments

UnAfraid picture UnAfraid  Â·  5Comments

kubistika picture kubistika  Â·  3Comments

Integralist picture Integralist  Â·  3Comments

rogierlommers picture rogierlommers  Â·  5Comments