Signed-off-by: Jim Minter <jminter@redhat.com>
| ... | ... |
@@ -18,12 +18,13 @@ import ( |
| 18 | 18 |
const name = "journald" |
| 19 | 19 |
|
| 20 | 20 |
type journald struct {
|
| 21 |
+ mu sync.Mutex |
|
| 21 | 22 |
vars map[string]string // additional variables and values to send to the journal along with the log message |
| 22 | 23 |
readers readerList |
| 24 |
+ closed bool |
|
| 23 | 25 |
} |
| 24 | 26 |
|
| 25 | 27 |
type readerList struct {
|
| 26 |
- mu sync.Mutex |
|
| 27 | 28 |
readers map[*logger.LogWatcher]*logger.LogWatcher |
| 28 | 29 |
} |
| 29 | 30 |
|
| ... | ... |
@@ -161,11 +161,12 @@ import ( |
| 161 | 161 |
) |
| 162 | 162 |
|
| 163 | 163 |
func (s *journald) Close() error {
|
| 164 |
- s.readers.mu.Lock() |
|
| 164 |
+ s.mu.Lock() |
|
| 165 |
+ s.closed = true |
|
| 165 | 166 |
for reader := range s.readers.readers {
|
| 166 | 167 |
reader.Close() |
| 167 | 168 |
} |
| 168 |
- s.readers.mu.Unlock() |
|
| 169 |
+ s.mu.Unlock() |
|
| 169 | 170 |
return nil |
| 170 | 171 |
} |
| 171 | 172 |
|
| ... | ... |
@@ -245,9 +246,16 @@ drain: |
| 245 | 245 |
} |
| 246 | 246 |
|
| 247 | 247 |
func (s *journald) followJournal(logWatcher *logger.LogWatcher, config logger.ReadConfig, j *C.sd_journal, pfd [2]C.int, cursor *C.char) *C.char {
|
| 248 |
- s.readers.mu.Lock() |
|
| 248 |
+ s.mu.Lock() |
|
| 249 | 249 |
s.readers.readers[logWatcher] = logWatcher |
| 250 |
- s.readers.mu.Unlock() |
|
| 250 |
+ if s.closed {
|
|
| 251 |
+ // the journald Logger is closed, presumably because the container has been |
|
| 252 |
+ // reset. So we shouldn't follow, because we'll never be woken up. But we |
|
| 253 |
+ // should make one more drainJournal call to be sure we've got all the logs. |
|
| 254 |
+ // Close pfd[1] so that one drainJournal happens, then cleanup, then return. |
|
| 255 |
+ C.close(pfd[1]) |
|
| 256 |
+ } |
|
| 257 |
+ s.mu.Unlock() |
|
| 251 | 258 |
|
| 252 | 259 |
newCursor := make(chan *C.char) |
| 253 | 260 |
|
| ... | ... |
@@ -274,22 +282,22 @@ func (s *journald) followJournal(logWatcher *logger.LogWatcher, config logger.Re |
| 274 | 274 |
|
| 275 | 275 |
// Clean up. |
| 276 | 276 |
C.close(pfd[0]) |
| 277 |
- s.readers.mu.Lock() |
|
| 277 |
+ s.mu.Lock() |
|
| 278 | 278 |
delete(s.readers.readers, logWatcher) |
| 279 |
- s.readers.mu.Unlock() |
|
| 279 |
+ s.mu.Unlock() |
|
| 280 | 280 |
close(logWatcher.Msg) |
| 281 | 281 |
newCursor <- cursor |
| 282 | 282 |
}() |
| 283 | 283 |
|
| 284 | 284 |
// Wait until we're told to stop. |
| 285 | 285 |
select {
|
| 286 |
+ case cursor = <-newCursor: |
|
| 286 | 287 |
case <-logWatcher.WatchClose(): |
| 287 | 288 |
// Notify the other goroutine that its work is done. |
| 288 | 289 |
C.close(pfd[1]) |
| 290 |
+ cursor = <-newCursor |
|
| 289 | 291 |
} |
| 290 | 292 |
|
| 291 |
- cursor = <-newCursor |
|
| 292 |
- |
|
| 293 | 293 |
return cursor |
| 294 | 294 |
} |
| 295 | 295 |
|
| ... | ... |
@@ -27,6 +27,7 @@ type JSONFileLogger struct {
|
| 27 | 27 |
mu sync.Mutex |
| 28 | 28 |
readers map[*logger.LogWatcher]struct{} // stores the active log followers
|
| 29 | 29 |
extra []byte // json-encoded extra attributes |
| 30 |
+ closed bool |
|
| 30 | 31 |
} |
| 31 | 32 |
|
| 32 | 33 |
func init() {
|
| ... | ... |
@@ -142,6 +143,7 @@ func (l *JSONFileLogger) LogPath() string {
|
| 142 | 142 |
// Close closes underlying file and signals all readers to stop. |
| 143 | 143 |
func (l *JSONFileLogger) Close() error {
|
| 144 | 144 |
l.mu.Lock() |
| 145 |
+ l.closed = true |
|
| 145 | 146 |
err := l.writer.Close() |
| 146 | 147 |
for r := range l.readers {
|
| 147 | 148 |
r.Close() |
| ... | ... |
@@ -88,10 +88,7 @@ func (l *JSONFileLogger) readLogs(logWatcher *logger.LogWatcher, config logger.R |
| 88 | 88 |
} |
| 89 | 89 |
} |
| 90 | 90 |
|
| 91 |
- if !config.Follow {
|
|
| 92 |
- if err := latestFile.Close(); err != nil {
|
|
| 93 |
- logrus.Errorf("Error closing file: %v", err)
|
|
| 94 |
- } |
|
| 91 |
+ if !config.Follow || l.closed {
|
|
| 95 | 92 |
l.mu.Unlock() |
| 96 | 93 |
return |
| 97 | 94 |
} |
| ... | ... |
@@ -100,17 +97,18 @@ func (l *JSONFileLogger) readLogs(logWatcher *logger.LogWatcher, config logger.R |
| 100 | 100 |
latestFile.Seek(0, os.SEEK_END) |
| 101 | 101 |
} |
| 102 | 102 |
|
| 103 |
+ notifyRotate := l.writer.NotifyRotate() |
|
| 104 |
+ defer l.writer.NotifyRotateEvict(notifyRotate) |
|
| 105 |
+ |
|
| 103 | 106 |
l.readers[logWatcher] = struct{}{}
|
| 107 |
+ |
|
| 104 | 108 |
l.mu.Unlock() |
| 105 | 109 |
|
| 106 |
- notifyRotate := l.writer.NotifyRotate() |
|
| 107 | 110 |
followLogs(latestFile, logWatcher, notifyRotate, config.Since) |
| 108 | 111 |
|
| 109 | 112 |
l.mu.Lock() |
| 110 | 113 |
delete(l.readers, logWatcher) |
| 111 | 114 |
l.mu.Unlock() |
| 112 |
- |
|
| 113 |
- l.writer.NotifyRotateEvict(notifyRotate) |
|
| 114 | 115 |
} |
| 115 | 116 |
|
| 116 | 117 |
func tailFile(f io.ReadSeeker, logWatcher *logger.LogWatcher, tail int, since time.Time) {
|
| ... | ... |
@@ -62,7 +62,7 @@ func (daemon *Daemon) ContainerLogs(ctx context.Context, containerName string, c |
| 62 | 62 |
return nil, logger.ErrReadLogsNotSupported |
| 63 | 63 |
} |
| 64 | 64 |
|
| 65 |
- follow := config.Follow && container.IsRunning() |
|
| 65 |
+ follow := config.Follow && !cLogCreated |
|
| 66 | 66 |
tailLines, err := strconv.Atoi(config.Tail) |
| 67 | 67 |
if err != nil {
|
| 68 | 68 |
tailLines = -1 |