github.com/kaisenlinux/docker@v0.0.0-20230510090727-ea55db55fac7/engine/daemon/logger/loggerutils/follow.go (about) 1 package loggerutils // import "github.com/docker/docker/daemon/logger/loggerutils" 2 3 import ( 4 "io" 5 "os" 6 "time" 7 8 "github.com/docker/docker/daemon/logger" 9 "github.com/docker/docker/pkg/filenotify" 10 "github.com/fsnotify/fsnotify" 11 "github.com/pkg/errors" 12 "github.com/sirupsen/logrus" 13 ) 14 15 var errRetry = errors.New("retry") 16 var errDone = errors.New("done") 17 18 type follow struct { 19 file *os.File 20 dec Decoder 21 fileWatcher filenotify.FileWatcher 22 logWatcher *logger.LogWatcher 23 notifyRotate, notifyEvict chan interface{} 24 oldSize int64 25 retries int 26 } 27 28 func (fl *follow) handleRotate() error { 29 name := fl.file.Name() 30 31 fl.file.Close() 32 fl.fileWatcher.Remove(name) 33 34 // retry when the file doesn't exist 35 var err error 36 for retries := 0; retries <= 5; retries++ { 37 f, err := open(name) 38 if err == nil || !os.IsNotExist(err) { 39 fl.file = f 40 break 41 } 42 } 43 if err != nil { 44 return err 45 } 46 if err := fl.fileWatcher.Add(name); err != nil { 47 return err 48 } 49 fl.dec.Reset(fl.file) 50 return nil 51 } 52 53 func (fl *follow) handleMustClose(evictErr error) { 54 fl.file.Close() 55 fl.dec.Close() 56 fl.logWatcher.Err <- errors.Wrap(evictErr, "log reader evicted due to errors") 57 logrus.WithField("file", fl.file.Name()).Error("Log reader notified that it must re-open log file, some log data may not be streamed to the client.") 58 } 59 60 func (fl *follow) waitRead() error { 61 select { 62 case e := <-fl.notifyEvict: 63 if e != nil { 64 err := e.(error) 65 fl.handleMustClose(err) 66 } 67 return errDone 68 case e := <-fl.fileWatcher.Events(): 69 switch e.Op { 70 case fsnotify.Write: 71 fl.dec.Reset(fl.file) 72 return nil 73 case fsnotify.Rename, fsnotify.Remove: 74 select { 75 case <-fl.notifyRotate: 76 case <-fl.logWatcher.WatchProducerGone(): 77 return errDone 78 case <-fl.logWatcher.WatchConsumerGone(): 79 return errDone 80 } 81 if err := fl.handleRotate(); err != nil { 82 return err 83 } 84 return nil 85 } 86 return errRetry 87 case err := <-fl.fileWatcher.Errors(): 88 logrus.Debugf("logger got error watching file: %v", err) 89 // Something happened, let's try and stay alive and create a new watcher 90 if fl.retries <= 5 { 91 fl.fileWatcher.Close() 92 fl.fileWatcher, err = watchFile(fl.file.Name()) 93 if err != nil { 94 return err 95 } 96 fl.retries++ 97 return errRetry 98 } 99 return err 100 case <-fl.logWatcher.WatchProducerGone(): 101 return errDone 102 case <-fl.logWatcher.WatchConsumerGone(): 103 return errDone 104 } 105 } 106 107 func (fl *follow) handleDecodeErr(err error) error { 108 if !errors.Is(err, io.EOF) { 109 return err 110 } 111 112 // Handle special case (#39235): max-file=1 and file was truncated 113 st, stErr := fl.file.Stat() 114 if stErr == nil { 115 size := st.Size() 116 defer func() { fl.oldSize = size }() 117 if size < fl.oldSize { // truncated 118 fl.file.Seek(0, 0) 119 fl.dec.Reset(fl.file) 120 return nil 121 } 122 } else { 123 logrus.WithError(stErr).Warn("logger: stat error") 124 } 125 126 for { 127 err := fl.waitRead() 128 if err == nil { 129 break 130 } 131 if err == errRetry { 132 continue 133 } 134 return err 135 } 136 return nil 137 } 138 139 func (fl *follow) mainLoop(since, until time.Time) { 140 for { 141 select { 142 case err := <-fl.notifyEvict: 143 if err != nil { 144 fl.handleMustClose(err.(error)) 145 } 146 return 147 default: 148 } 149 msg, err := fl.dec.Decode() 150 if err != nil { 151 if err := fl.handleDecodeErr(err); err != nil { 152 if err == errDone { 153 return 154 } 155 // we got an unrecoverable error, so return 156 fl.logWatcher.Err <- err 157 return 158 } 159 // ready to try again 160 continue 161 } 162 163 fl.retries = 0 // reset retries since we've succeeded 164 if !since.IsZero() && msg.Timestamp.Before(since) { 165 continue 166 } 167 if !until.IsZero() && msg.Timestamp.After(until) { 168 return 169 } 170 // send the message, unless the consumer is gone 171 select { 172 case e := <-fl.notifyEvict: 173 if e != nil { 174 err := e.(error) 175 logrus.WithError(err).Debug("Reader evicted while sending log message") 176 fl.logWatcher.Err <- err 177 } 178 return 179 case fl.logWatcher.Msg <- msg: 180 case <-fl.logWatcher.WatchConsumerGone(): 181 return 182 } 183 } 184 } 185 186 func followLogs(f *os.File, logWatcher *logger.LogWatcher, notifyRotate, notifyEvict chan interface{}, dec Decoder, since, until time.Time) { 187 dec.Reset(f) 188 189 name := f.Name() 190 fileWatcher, err := watchFile(name) 191 if err != nil { 192 logWatcher.Err <- err 193 return 194 } 195 defer func() { 196 f.Close() 197 dec.Close() 198 fileWatcher.Close() 199 }() 200 201 fl := &follow{ 202 file: f, 203 oldSize: -1, 204 logWatcher: logWatcher, 205 fileWatcher: fileWatcher, 206 notifyRotate: notifyRotate, 207 notifyEvict: notifyEvict, 208 dec: dec, 209 } 210 fl.mainLoop(since, until) 211 }