From 9cd24ba6057fa479918e9bce4e2c0554ec991394 Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Fri, 20 Sep 2019 15:50:10 -0700 Subject: [PATCH] logger: fix follow logs for max-file=1 In case jsonlogfile is used with max-file=1 and max-size set, the log rotation is not perfomed; instead, the log file is closed and re-open with O_TRUNC. This situation is not handled by the log reader in follow mode, leading to an issue of log reader being stuck forever. This situation (file close/reopen) could be handled in waitRead(), but fsnotify library chose to not listen to or deliver this event (IN_CLOSE_WRITE in inotify lingo). So, we have to handle this by checking the file size upon receiving io.EOF from the log reader, and comparing the size with the one received earlier. In case the new size is less than the old one, the file was truncated and we need to seek to its beginning. Fixes #39235. Signed-off-by: Kir Kolyshkin --- daemon/logger/loggerutils/logfile.go | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/daemon/logger/loggerutils/logfile.go b/daemon/logger/loggerutils/logfile.go index fa45154ec9..cfc4a2e210 100644 --- a/daemon/logger/loggerutils/logfile.go +++ b/daemon/logger/loggerutils/logfile.go @@ -265,7 +265,7 @@ func compressFile(fileName string, lastTimestamp time.Time) { compressWriter := gzip.NewWriter(outFile) defer compressWriter.Close() - // Add the last log entry timestramp to the gzip header + // Add the last log entry timestamp to the gzip header extra := rotateFileMetadata{} extra.LastTime = lastTimestamp compressWriter.Header.Extra, err = json.Marshal(&extra) @@ -614,11 +614,25 @@ func followLogs(f *os.File, logWatcher *logger.LogWatcher, notifyRotate chan int } } + oldSize := int64(-1) handleDecodeErr := func(err error) error { if errors.Cause(err) != io.EOF { return err } + // Handle special case (#39235): max-file=1 and file was truncated + st, stErr := f.Stat() + if stErr == nil { + size := st.Size() + defer func() { oldSize = size }() + if size < oldSize { // truncated + f.Seek(0, 0) + return nil + } + } else { + logrus.WithError(stErr).Warn("logger: stat error") + } + for { err := waitRead() if err == nil {