github.com/kyma-project/kyma-environment-broker@v0.0.1/internal/logger/spy_logger.go (about)

     1  // Package copied from https://github.com/kyma-project/kyma/blob/1.11.0/components/service-binding-usage-controller/internal/platform/logger/spy/logger.go
     2  // Only Reset() method was added.
     3  package logger
     4  
     5  import (
     6  	"bufio"
     7  	"bytes"
     8  	"encoding/json"
     9  	"io/ioutil"
    10  	"strings"
    11  	"testing"
    12  
    13  	"github.com/sirupsen/logrus"
    14  )
    15  
    16  // LogSpy is a helper construct for testing logging in unit tests.
    17  // Beware: all methods are working on copies of of original messages buffer and are safe for multiple uses.
    18  type LogSpy struct {
    19  	buffer    *bytes.Buffer
    20  	RawLogger *logrus.Logger
    21  	Logger    *logrus.Entry
    22  }
    23  
    24  // NewLogSpy is a factory for LogSpy
    25  func NewLogSpy() *LogSpy {
    26  	buffer := bytes.NewBuffer([]byte(""))
    27  
    28  	rawLgr := &logrus.Logger{
    29  		Out: buffer,
    30  		// standard json formatter is used to ease testing
    31  		Formatter: new(logrus.JSONFormatter),
    32  		Hooks:     make(logrus.LevelHooks),
    33  		Level:     logrus.DebugLevel,
    34  	}
    35  
    36  	lgr := rawLgr.WithField("testing", true)
    37  
    38  	return &LogSpy{
    39  		buffer:    buffer,
    40  		RawLogger: rawLgr,
    41  		Logger:    lgr,
    42  	}
    43  }
    44  
    45  // AssertErrorLogged checks whatever a specific string was logged as error.
    46  //
    47  // Compared elements: level, message
    48  //
    49  // Wrapped errors are supported as long as original error message ends up in resulting one.
    50  func (s *LogSpy) AssertErrorLogged(t *testing.T, errorExpected error) {
    51  	if !s.wasLogged(t, logrus.ErrorLevel, errorExpected.Error()) {
    52  		t.Errorf("error was not logged, expected: \"%s\"", errorExpected.Error())
    53  	}
    54  }
    55  
    56  // AssertLogged checks whatever a specific string was logged at a specific level.
    57  //
    58  // Compared elements: level, message
    59  //
    60  // Beware: we are checking for sub-strings and not for the exact match.
    61  func (s *LogSpy) AssertLogged(t *testing.T, level logrus.Level, message string) {
    62  	if !s.wasLogged(t, level, message) {
    63  		t.Errorf("message was not logged, message: \"%s\", level: %s", message, level)
    64  	}
    65  }
    66  
    67  // AssertNotLogged checks whatever a specific string was not logged at a specific level.
    68  //
    69  // Compared elements: level, message
    70  //
    71  // Beware: we are checking for sub-strings and not for the exact match.
    72  func (s *LogSpy) AssertNotLogged(t *testing.T, level logrus.Level, message string) {
    73  	if s.wasLogged(t, level, message) {
    74  		t.Errorf("message was logged, message: \"%s\", level: %s", message, level)
    75  	}
    76  }
    77  
    78  // wasLogged checks whatever a message was logged.
    79  //
    80  // Compared elements: level, message
    81  func (s *LogSpy) wasLogged(t *testing.T, level logrus.Level, message string) bool {
    82  	// new reader is created so we are safe for multiple reads
    83  	buf := bytes.NewReader(s.buffer.Bytes())
    84  	scanner := bufio.NewScanner(buf)
    85  	var entryPartial struct {
    86  		Level string `json:"level"`
    87  		Msg   string `json:"msg"`
    88  	}
    89  
    90  	for scanner.Scan() {
    91  		line := scanner.Text()
    92  
    93  		err := json.Unmarshal([]byte(line), &entryPartial)
    94  		if err != nil {
    95  			t.Fatalf("unexpected error on log line unmarshalling, line: %s", line)
    96  		}
    97  
    98  		levelMatches := entryPartial.Level == level.String()
    99  
   100  		// We are looking only if expected is contained (as opposed to exact match check),
   101  		// so that e.g. errors wrapping is supported.
   102  		containsMessage := strings.Contains(entryPartial.Msg, message)
   103  
   104  		if levelMatches && containsMessage {
   105  			return true
   106  		}
   107  	}
   108  
   109  	return false
   110  }
   111  
   112  // DumpAll returns all logged messages.
   113  func (s *LogSpy) DumpAll() []string {
   114  	// new reader is created so we are safe for multiple reads
   115  	buf := bytes.NewReader(s.buffer.Bytes())
   116  	scanner := bufio.NewScanner(buf)
   117  
   118  	out := []string{}
   119  	for scanner.Scan() {
   120  		out = append(out, scanner.Text())
   121  	}
   122  
   123  	return out
   124  }
   125  
   126  // Reset removes logged messages.
   127  func (s *LogSpy) Reset() {
   128  	s.buffer.Reset()
   129  }
   130  
   131  // NewLogDummy returns dummy logger which discards logged messages on the fly.
   132  // Useful when logger is required as dependency in unit testing.
   133  func NewLogDummy() *logrus.Entry {
   134  	rawLgr := logrus.New()
   135  	rawLgr.Out = ioutil.Discard
   136  	lgr := rawLgr.WithField("testing", true)
   137  
   138  	return lgr
   139  }