github.com/letsencrypt/boulder@v0.20251208.0/log/mock.go (about) 1 package log 2 3 import ( 4 "fmt" 5 "log/syslog" 6 "regexp" 7 "strings" 8 ) 9 10 // UseMock sets a mock logger as the default logger, and returns it. 11 func UseMock() *Mock { 12 m := NewMock() 13 _ = Set(m) 14 return m 15 } 16 17 // NewMock creates a mock logger. 18 func NewMock() *Mock { 19 return &Mock{impl{newMockWriter()}} 20 } 21 22 // Mock is a logger that stores all log messages in memory to be examined by a 23 // test. 24 type Mock struct { 25 impl 26 } 27 28 // WaitingMock is a logger that stores all messages in memory to be examined by a test with methods 29 type WaitingMock struct { 30 impl 31 } 32 33 // Mock implements the writer interface. It 34 // stores all logged messages in a buffer for inspection by test 35 // functions (via GetAll()) instead of sending them to syslog. 36 type mockWriter struct { 37 logged []string 38 msgChan chan<- string 39 getChan <-chan []string 40 clearChan chan<- struct{} 41 closeChan chan<- struct{} 42 } 43 44 var levelName = map[syslog.Priority]string{ 45 syslog.LOG_ERR: "ERR", 46 syslog.LOG_WARNING: "WARNING", 47 syslog.LOG_INFO: "INFO", 48 syslog.LOG_DEBUG: "DEBUG", 49 } 50 51 func (w *mockWriter) logAtLevel(p syslog.Priority, msg string, a ...any) { 52 w.msgChan <- fmt.Sprintf("%s: %s", levelName[p&7], fmt.Sprintf(msg, a...)) 53 } 54 55 // newMockWriter returns a new mockWriter 56 func newMockWriter() *mockWriter { 57 msgChan := make(chan string) 58 getChan := make(chan []string) 59 clearChan := make(chan struct{}) 60 closeChan := make(chan struct{}) 61 w := &mockWriter{ 62 logged: []string{}, 63 msgChan: msgChan, 64 getChan: getChan, 65 clearChan: clearChan, 66 closeChan: closeChan, 67 } 68 go func() { 69 for { 70 select { 71 case logMsg := <-msgChan: 72 w.logged = append(w.logged, logMsg) 73 case getChan <- w.logged: 74 case <-clearChan: 75 w.logged = []string{} 76 case <-closeChan: 77 close(getChan) 78 return 79 } 80 } 81 }() 82 return w 83 } 84 85 // GetAll returns all messages logged since instantiation or the last call to 86 // Clear(). 87 // 88 // The caller must not modify the returned slice or its elements. 89 func (m *Mock) GetAll() []string { 90 w := m.w.(*mockWriter) 91 return <-w.getChan 92 } 93 94 // GetAllMatching returns all messages logged since instantiation or the last 95 // Clear() whose text matches the given regexp. The regexp is 96 // accepted as a string and compiled on the fly, because convenience 97 // is more important than performance. 98 // 99 // The caller must not modify the elements of the returned slice. 100 func (m *Mock) GetAllMatching(reString string) []string { 101 var matches []string 102 w := m.w.(*mockWriter) 103 re := regexp.MustCompile(reString) 104 for _, logMsg := range <-w.getChan { 105 if re.MatchString(logMsg) { 106 matches = append(matches, logMsg) 107 } 108 } 109 return matches 110 } 111 112 func (m *Mock) ExpectMatch(reString string) error { 113 results := m.GetAllMatching(reString) 114 if len(results) == 0 { 115 return fmt.Errorf("expected log line %q, got %q", reString, strings.Join(m.GetAll(), "\n")) 116 } 117 return nil 118 } 119 120 // Clear resets the log buffer. 121 func (m *Mock) Clear() { 122 w := m.w.(*mockWriter) 123 w.clearChan <- struct{}{} 124 }