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 }