Skip to content

Commit 84c2507

Browse files
committed
add journald support
1 parent 6af7bbb commit 84c2507

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+2668
-3906
lines changed

Godeps/Godeps.json

Lines changed: 32 additions & 54 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/kernelmonitor/kernel_log_watcher.go

Lines changed: 81 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,20 @@ limitations under the License.
1717
package kernelmonitor
1818

1919
import (
20+
"bufio"
21+
"bytes"
22+
"io"
2023
"os"
24+
"strings"
2125
"time"
2226

2327
"k8s.io/node-problem-detector/pkg/kernelmonitor/translator"
2428
"k8s.io/node-problem-detector/pkg/kernelmonitor/types"
2529
"k8s.io/node-problem-detector/pkg/kernelmonitor/util"
2630

31+
"github.com/coreos/go-systemd/sdjournal"
2732
"github.com/golang/glog"
28-
"github.com/hpcloud/tail"
33+
"github.com/google/cadvisor/utils/tail"
2934
utilclock "github.com/pivotal-golang/clock"
3035
)
3136

@@ -54,12 +59,12 @@ type KernelLogWatcher interface {
5459

5560
type kernelLogWatcher struct {
5661
// trans is the translator translates the log into internal format.
57-
trans translator.Translator
58-
cfg WatcherConfig
59-
tl *tail.Tail
60-
logCh chan *types.KernelLog
61-
tomb *util.Tomb
62-
clock utilclock.Clock
62+
trans translator.Translator
63+
cfg WatcherConfig
64+
reader *bufio.Reader
65+
logCh chan *types.KernelLog
66+
tomb *util.Tomb
67+
clock utilclock.Clock
6368
}
6469

6570
// NewKernelLogWatcher creates a new kernel log watcher.
@@ -88,25 +93,17 @@ func (k *kernelLogWatcher) Watch() (<-chan *types.KernelLog, error) {
8893
// To avoid this, we decide to add this temporarily hack. When KernelMonitor can't find the kernel
8994
// log file, it will print a log and then return nil channel and no error. Since nil channel will
9095
// always be blocked, the NodeProblemDetector will block forever.
91-
// TODO(random-liu):
92-
// 1. Add journald supports to support GCI.
93-
// 2. Schedule KernelMonitor only on supported node (with node label and selector)
9496
if _, err := os.Stat(path); os.IsNotExist(err) {
9597
glog.Infof("kernel log %q is not found, kernel monitor doesn't support the os distro", path)
9698
return nil, nil
9799
}
98100
// TODO(random-liu): Rate limit tail file.
99-
// TODO(random-liu): Figure out what happens if log lines are removed.
100101
// Notice that, kernel log watcher doesn't look back to the rolled out logs.
101-
var err error
102-
k.tl, err = tail.TailFile(path, tail.Config{
103-
Poll: true,
104-
ReOpen: true,
105-
Follow: true,
106-
})
102+
reader, err := getLogReader(path)
107103
if err != nil {
108104
return nil, err
109105
}
106+
k.reader = bufio.NewReader(reader)
110107
glog.Info("Start watching kernel log")
111108
go k.watchLoop()
112109
return k.logCh, nil
@@ -126,15 +123,31 @@ func (k *kernelLogWatcher) watchLoop() {
126123
if err != nil {
127124
glog.Fatalf("failed to parse duration %q: %v", k.cfg.Lookback, err)
128125
}
126+
var buffer bytes.Buffer
129127
for {
128+
130129
select {
131-
case line := <-k.tl.Lines:
132-
// Notice that tail has trimmed '\n'
133-
if line.Err != nil {
134-
glog.Errorf("Tail error: %v", line.Err)
135-
continue
136-
}
137-
log, err := k.trans.Translate(line.Text)
130+
case <-k.tomb.Stopping():
131+
glog.Infof("Stop watching kernel log")
132+
return
133+
default:
134+
}
135+
136+
line, err := k.reader.ReadString('\n')
137+
if err != nil && err != io.EOF {
138+
glog.Errorf("exiting kernel log watch with error: %v", err)
139+
return
140+
}
141+
if line == "" {
142+
time.Sleep(100 * time.Millisecond)
143+
continue
144+
}
145+
if err == nil {
146+
buffer.WriteString(line)
147+
// trime `\n`
148+
line = strings.TrimRight(buffer.String(), "\n")
149+
buffer.Reset()
150+
log, err := k.trans.Translate(line)
138151
if err != nil {
139152
glog.Infof("Unable to parse line: %q, %v", line, err)
140153
continue
@@ -144,14 +157,54 @@ func (k *kernelLogWatcher) watchLoop() {
144157
continue
145158
}
146159
k.logCh <- log
147-
case <-k.tomb.Stopping():
148-
k.tl.Stop()
149-
glog.Infof("Stop watching kernel log")
150-
return
160+
} else { // err == io.EOF
161+
buffer.WriteString(line)
151162
}
152163
}
153164
}
154165

166+
// getLogReader gets a kernel log reader.
167+
func getLogReader(path string) (io.Reader, error) {
168+
reader, err := tryJournal()
169+
if err == nil {
170+
return reader, nil
171+
}
172+
reader, err = tryLogFile(path)
173+
if err == nil {
174+
return reader, nil
175+
}
176+
return nil, err
177+
}
178+
179+
func tryJournal() (io.Reader, error) {
180+
r, err := sdjournal.NewJournalReader(sdjournal.JournalReaderConfig{
181+
NumFromTail: uint64(0),
182+
Matches: []sdjournal.Match{
183+
{
184+
Field: sdjournal.SD_JOURNAL_FIELD_TRANSPORT,
185+
Value: "kernel",
186+
},
187+
},
188+
})
189+
if err != nil {
190+
return nil, fmt.Errorf("Error opening journal: %v", err)
191+
}
192+
if r == nil {
193+
return nil, fmt.Errorf("Got a nil reader")
194+
}
195+
glog.Info("Kernel log watcher use journal")
196+
return r, nil
197+
}
198+
199+
func tryLogFile(path string) (io.Reader, error) {
200+
tail, err := tail.NewTail(path)
201+
if err != nil {
202+
return nil, err
203+
}
204+
glog.Infof("Kernel log watcher use log file: %s", path)
205+
return tail, nil
206+
}
207+
155208
func parseDuration(s string) (time.Duration, error) {
156209
// If the duration is not configured, just return 0 by default
157210
if s == "" {

0 commit comments

Comments
 (0)