Skip to content

Commit 6ffde06

Browse files
committed
JournalReader: fixes in JournalReader.Read()
This patch fixes two issues in JournalReader.Read(): 1. The return value of copy() was ignored and JournalReader.Read() was returning the wrong amount of bytes copied. 2. JournalReader implements the Reader interface so it must be able to read a specific amount of bytes. Journald on the other hand only allows us to read full entries of arbitrary size (without byte granularity). JournalReader therefore needs to internally buffer entries that don't fit in the read buffer. Callers should keep calling until 0 and/or an error is returned. Fixes #172
1 parent 6dc8b84 commit 6ffde06

File tree

1 file changed

+48
-20
lines changed

1 file changed

+48
-20
lines changed

sdjournal/read.go

Lines changed: 48 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"fmt"
2121
"io"
2222
"log"
23+
"strings"
2324
"time"
2425
)
2526

@@ -46,9 +47,10 @@ type JournalReaderConfig struct {
4647
}
4748

4849
// JournalReader is an io.ReadCloser which provides a simple interface for iterating through the
49-
// systemd journal.
50+
// systemd journal. A JournalReader is not safe for concurrent use by multiple goroutines.
5051
type JournalReader struct {
51-
journal *Journal
52+
journal *Journal
53+
msgReader *strings.Reader
5254
}
5355

5456
// NewJournalReader creates a new JournalReader with configuration options that are similar to the
@@ -101,35 +103,60 @@ func NewJournalReader(config JournalReaderConfig) (*JournalReader, error) {
101103
return r, nil
102104
}
103105

106+
// Read reads entries from the journal. Read follows the Reader interface so
107+
// it must be able to read a specific amount of bytes. Journald on the other
108+
// hand only allows us to read full entries of arbitrary size (without byte
109+
// granularity). JournalReader is therefore internally buffering entries that
110+
// don't fit in the read buffer. Callers should keep calling until 0 and/or an
111+
// error is returned.
104112
func (r *JournalReader) Read(b []byte) (int, error) {
105113
var err error
106-
var c int
107114

108-
// Advance the journal cursor
109-
c, err = r.journal.Next()
115+
if r.msgReader == nil {
116+
var c int
110117

111-
// An unexpected error
112-
if err != nil {
113-
return 0, err
114-
}
118+
// Advance the journal cursor. It has to be called at least one time
119+
// before reading
120+
c, err = r.journal.Next()
115121

116-
// EOF detection
117-
if c == 0 {
118-
return 0, io.EOF
119-
}
122+
// An unexpected error
123+
if err != nil {
124+
return 0, err
125+
}
120126

121-
// Build a message
122-
var msg string
123-
msg, err = r.buildMessage()
127+
// EOF detection
128+
if c == 0 {
129+
return 0, io.EOF
130+
}
124131

125-
if err != nil {
126-
return 0, err
132+
// Build a message
133+
var msg string
134+
msg, err = r.buildMessage()
135+
136+
if err != nil {
137+
return 0, err
138+
}
139+
r.msgReader = strings.NewReader(msg)
127140
}
128141

129142
// Copy and return the message
130-
copy(b, []byte(msg))
143+
var sz int
144+
sz, err = r.msgReader.Read(b)
145+
if err == io.EOF {
146+
// The current entry has been fully read. Don't propagate this
147+
// EOF, so the next entry can be read at the next Read()
148+
// iteration.
149+
r.msgReader = nil
150+
return sz, nil
151+
}
152+
if err != nil {
153+
return sz, err
154+
}
155+
if r.msgReader.Len() == 0 {
156+
r.msgReader = nil
157+
}
131158

132-
return len(msg), nil
159+
return sz, nil
133160
}
134161

135162
// Close closes the JournalReader's handle to the journal.
@@ -139,6 +166,7 @@ func (r *JournalReader) Close() error {
139166

140167
// Rewind attempts to rewind the JournalReader to the first entry.
141168
func (r *JournalReader) Rewind() error {
169+
r.msgReader = nil
142170
return r.journal.SeekHead()
143171
}
144172

0 commit comments

Comments
 (0)