|
@@ -3,7 +3,6 @@ package jsonfilelog
|
|
import (
|
|
import (
|
|
"bytes"
|
|
"bytes"
|
|
"encoding/json"
|
|
"encoding/json"
|
|
- "errors"
|
|
|
|
"fmt"
|
|
"fmt"
|
|
"io"
|
|
"io"
|
|
"os"
|
|
"os"
|
|
@@ -18,6 +17,7 @@ import (
|
|
"github.com/docker/docker/pkg/filenotify"
|
|
"github.com/docker/docker/pkg/filenotify"
|
|
"github.com/docker/docker/pkg/jsonlog"
|
|
"github.com/docker/docker/pkg/jsonlog"
|
|
"github.com/docker/docker/pkg/tailfile"
|
|
"github.com/docker/docker/pkg/tailfile"
|
|
|
|
+ "github.com/pkg/errors"
|
|
)
|
|
)
|
|
|
|
|
|
const maxJSONDecodeRetry = 20000
|
|
const maxJSONDecodeRetry = 20000
|
|
@@ -48,10 +48,11 @@ func (l *JSONFileLogger) ReadLogs(config logger.ReadConfig) *logger.LogWatcher {
|
|
func (l *JSONFileLogger) readLogs(logWatcher *logger.LogWatcher, config logger.ReadConfig) {
|
|
func (l *JSONFileLogger) readLogs(logWatcher *logger.LogWatcher, config logger.ReadConfig) {
|
|
defer close(logWatcher.Msg)
|
|
defer close(logWatcher.Msg)
|
|
|
|
|
|
- // lock so the read stream doesn't get corrupted due to rotations or other log data written while we read
|
|
|
|
|
|
+ // lock so the read stream doesn't get corrupted due to rotations or other log data written while we open these files
|
|
// This will block writes!!!
|
|
// This will block writes!!!
|
|
- l.mu.Lock()
|
|
|
|
|
|
+ l.mu.RLock()
|
|
|
|
|
|
|
|
+ // TODO it would be nice to move a lot of this reader implementation to the rotate logger object
|
|
pth := l.writer.LogPath()
|
|
pth := l.writer.LogPath()
|
|
var files []io.ReadSeeker
|
|
var files []io.ReadSeeker
|
|
for i := l.writer.MaxFiles(); i > 1; i-- {
|
|
for i := l.writer.MaxFiles(); i > 1; i-- {
|
|
@@ -59,25 +60,36 @@ func (l *JSONFileLogger) readLogs(logWatcher *logger.LogWatcher, config logger.R
|
|
if err != nil {
|
|
if err != nil {
|
|
if !os.IsNotExist(err) {
|
|
if !os.IsNotExist(err) {
|
|
logWatcher.Err <- err
|
|
logWatcher.Err <- err
|
|
- break
|
|
|
|
|
|
+ l.mu.RUnlock()
|
|
|
|
+ return
|
|
}
|
|
}
|
|
continue
|
|
continue
|
|
}
|
|
}
|
|
defer f.Close()
|
|
defer f.Close()
|
|
-
|
|
|
|
files = append(files, f)
|
|
files = append(files, f)
|
|
}
|
|
}
|
|
|
|
|
|
latestFile, err := os.Open(pth)
|
|
latestFile, err := os.Open(pth)
|
|
if err != nil {
|
|
if err != nil {
|
|
- logWatcher.Err <- err
|
|
|
|
- l.mu.Unlock()
|
|
|
|
|
|
+ logWatcher.Err <- errors.Wrap(err, "error opening latest log file")
|
|
|
|
+ l.mu.RUnlock()
|
|
return
|
|
return
|
|
}
|
|
}
|
|
defer latestFile.Close()
|
|
defer latestFile.Close()
|
|
|
|
|
|
|
|
+ latestChunk, err := newSectionReader(latestFile)
|
|
|
|
+
|
|
|
|
+ // Now we have the reader sectioned, all fd's opened, we can unlock.
|
|
|
|
+ // New writes/rotates will not affect seeking through these files
|
|
|
|
+ l.mu.RUnlock()
|
|
|
|
+
|
|
|
|
+ if err != nil {
|
|
|
|
+ logWatcher.Err <- err
|
|
|
|
+ return
|
|
|
|
+ }
|
|
|
|
+
|
|
if config.Tail != 0 {
|
|
if config.Tail != 0 {
|
|
- tailer := multireader.MultiReadSeeker(append(files, latestFile)...)
|
|
|
|
|
|
+ tailer := multireader.MultiReadSeeker(append(files, latestChunk)...)
|
|
tailFile(tailer, logWatcher, config.Tail, config.Since)
|
|
tailFile(tailer, logWatcher, config.Tail, config.Since)
|
|
}
|
|
}
|
|
|
|
|
|
@@ -89,19 +101,14 @@ func (l *JSONFileLogger) readLogs(logWatcher *logger.LogWatcher, config logger.R
|
|
}
|
|
}
|
|
|
|
|
|
if !config.Follow || l.closed {
|
|
if !config.Follow || l.closed {
|
|
- l.mu.Unlock()
|
|
|
|
return
|
|
return
|
|
}
|
|
}
|
|
|
|
|
|
- if config.Tail >= 0 {
|
|
|
|
- latestFile.Seek(0, os.SEEK_END)
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
notifyRotate := l.writer.NotifyRotate()
|
|
notifyRotate := l.writer.NotifyRotate()
|
|
defer l.writer.NotifyRotateEvict(notifyRotate)
|
|
defer l.writer.NotifyRotateEvict(notifyRotate)
|
|
|
|
|
|
|
|
+ l.mu.Lock()
|
|
l.readers[logWatcher] = struct{}{}
|
|
l.readers[logWatcher] = struct{}{}
|
|
-
|
|
|
|
l.mu.Unlock()
|
|
l.mu.Unlock()
|
|
|
|
|
|
followLogs(latestFile, logWatcher, notifyRotate, config.Since)
|
|
followLogs(latestFile, logWatcher, notifyRotate, config.Since)
|
|
@@ -111,6 +118,16 @@ func (l *JSONFileLogger) readLogs(logWatcher *logger.LogWatcher, config logger.R
|
|
l.mu.Unlock()
|
|
l.mu.Unlock()
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+func newSectionReader(f *os.File) (*io.SectionReader, error) {
|
|
|
|
+ // seek to the end to get the size
|
|
|
|
+ // we'll leave this at the end of the file since section reader does not advance the reader
|
|
|
|
+ size, err := f.Seek(0, os.SEEK_END)
|
|
|
|
+ if err != nil {
|
|
|
|
+ return nil, errors.Wrap(err, "error getting current file size")
|
|
|
|
+ }
|
|
|
|
+ return io.NewSectionReader(f, 0, size), nil
|
|
|
|
+}
|
|
|
|
+
|
|
func tailFile(f io.ReadSeeker, logWatcher *logger.LogWatcher, tail int, since time.Time) {
|
|
func tailFile(f io.ReadSeeker, logWatcher *logger.LogWatcher, tail int, since time.Time) {
|
|
var rdr io.Reader
|
|
var rdr io.Reader
|
|
rdr = f
|
|
rdr = f
|