Skip to content

Commit 75d6553

Browse files
authored
Merge pull request moby#40920 from cpuguy83/log_rotate_error_handling
logfile: Check if log is closed on close error during rotate
2 parents daa826f + 3989f91 commit 75d6553

File tree

2 files changed

+69
-2
lines changed

2 files changed

+69
-2
lines changed

daemon/logger/loggerutils/logfile.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -188,8 +188,11 @@ func (w *LogFile) checkCapacityAndRotate() error {
188188
w.rotateMu.Lock()
189189
fname := w.f.Name()
190190
if err := w.f.Close(); err != nil {
191-
w.rotateMu.Unlock()
192-
return errors.Wrap(err, "error closing file")
191+
// if there was an error during a prior rotate, the file could already be closed
192+
if !errors.Is(err, os.ErrClosed) {
193+
w.rotateMu.Unlock()
194+
return errors.Wrap(err, "error closing file")
195+
}
193196
}
194197
if err := rotate(fname, w.maxFiles, w.compress); err != nil {
195198
w.rotateMu.Unlock()

daemon/logger/loggerutils/logfile_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"time"
1212

1313
"github.com/docker/docker/daemon/logger"
14+
"github.com/docker/docker/pkg/pubsub"
1415
"github.com/docker/docker/pkg/tailfile"
1516
"gotest.tools/v3/assert"
1617
)
@@ -235,3 +236,66 @@ func TestFollowLogsProducerGone(t *testing.T) {
235236
default:
236237
}
237238
}
239+
240+
func TestCheckCapacityAndRotate(t *testing.T) {
241+
dir, err := ioutil.TempDir("", t.Name())
242+
assert.NilError(t, err)
243+
defer os.RemoveAll(dir)
244+
245+
f, err := ioutil.TempFile(dir, "log")
246+
assert.NilError(t, err)
247+
248+
l := &LogFile{
249+
f: f,
250+
capacity: 5,
251+
maxFiles: 3,
252+
compress: true,
253+
notifyRotate: pubsub.NewPublisher(0, 1),
254+
perms: 0600,
255+
marshal: func(msg *logger.Message) ([]byte, error) {
256+
return msg.Line, nil
257+
},
258+
}
259+
defer l.Close()
260+
261+
assert.NilError(t, l.WriteLogEntry(&logger.Message{Line: []byte("hello world!")}))
262+
263+
dStringer := dirStringer{dir}
264+
265+
_, err = os.Stat(f.Name() + ".1")
266+
assert.Assert(t, os.IsNotExist(err), dStringer)
267+
268+
assert.NilError(t, l.WriteLogEntry(&logger.Message{Line: []byte("hello world!")}))
269+
_, err = os.Stat(f.Name() + ".1")
270+
assert.NilError(t, err, dStringer)
271+
272+
assert.NilError(t, l.WriteLogEntry(&logger.Message{Line: []byte("hello world!")}))
273+
_, err = os.Stat(f.Name() + ".1")
274+
assert.NilError(t, err, dStringer)
275+
_, err = os.Stat(f.Name() + ".2.gz")
276+
assert.NilError(t, err, dStringer)
277+
278+
// Now let's simulate a failed rotation where the file was able to be closed but something else happened elsewhere
279+
// down the line.
280+
// We want to make sure that we can recover in the case that `l.f` was closed while attempting a rotation.
281+
l.f.Close()
282+
assert.NilError(t, l.WriteLogEntry(&logger.Message{Line: []byte("hello world!")}))
283+
}
284+
285+
type dirStringer struct {
286+
d string
287+
}
288+
289+
func (d dirStringer) String() string {
290+
ls, err := ioutil.ReadDir(d.d)
291+
if err != nil {
292+
return ""
293+
}
294+
var s strings.Builder
295+
s.WriteString("\n")
296+
297+
for _, fi := range ls {
298+
s.WriteString(fi.Name() + "\n")
299+
}
300+
return s.String()
301+
}

0 commit comments

Comments
 (0)