Skip to content

Commit f3ab10e

Browse files
authored
Merge pull request #442 from abansal4032/custom-plugin-logs-capture
Capture the logs from stderr of custom plugins
2 parents c3cf941 + 6acf5b1 commit f3ab10e

File tree

2 files changed

+76
-4
lines changed

2 files changed

+76
-4
lines changed

pkg/custompluginmonitor/plugin/plugin.go

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ package plugin
1919
import (
2020
"context"
2121
"fmt"
22+
"io"
23+
"io/ioutil"
2224
"os/exec"
2325
"strings"
2426
"sync"
@@ -30,6 +32,10 @@ import (
3032
"k8s.io/node-problem-detector/pkg/util/tomb"
3133
)
3234

35+
// maxCustomPluginBufferBytes is the max bytes that a custom plugin is allowed to
36+
// send to stdout/stderr. Any bytes exceeding this value will be truncated.
37+
const maxCustomPluginBufferBytes = 1024 * 4
38+
3339
type Plugin struct {
3440
config cpmtypes.CustomPluginConfig
3541
syncChan chan struct{}
@@ -117,6 +123,20 @@ func (p *Plugin) runRules() {
117123
glog.Info("Finish running custom plugins")
118124
}
119125

126+
// readFromReader reads the maxBytes from the reader and drains the rest.
127+
func readFromReader(reader io.ReadCloser, maxBytes int64) ([]byte, error) {
128+
limitReader := io.LimitReader(reader, maxBytes)
129+
data, err := ioutil.ReadAll(limitReader)
130+
if err != nil {
131+
return []byte{}, err
132+
}
133+
// Drain the reader
134+
if _, err := io.Copy(ioutil.Discard, reader); err != nil {
135+
return []byte{}, err
136+
}
137+
return data, nil
138+
}
139+
120140
func (p *Plugin) run(rule cpmtypes.CustomRule) (exitStatus cpmtypes.Status, output string) {
121141
var ctx context.Context
122142
var cancel context.CancelFunc
@@ -129,14 +149,66 @@ func (p *Plugin) run(rule cpmtypes.CustomRule) (exitStatus cpmtypes.Status, outp
129149
defer cancel()
130150

131151
cmd := exec.CommandContext(ctx, rule.Path, rule.Args...)
132-
stdout, err := cmd.Output()
152+
153+
stdoutPipe, err := cmd.StdoutPipe()
154+
if err != nil {
155+
glog.Errorf("Error creating stdout pipe for plugin %q: error - %v", rule.Path, err)
156+
return cpmtypes.Unknown, "Error creating stdout pipe for plugin. Please check the error log"
157+
}
158+
stderrPipe, err := cmd.StderrPipe()
133159
if err != nil {
160+
glog.Errorf("Error creating stderr pipe for plugin %q: error - %v", rule.Path, err)
161+
return cpmtypes.Unknown, "Error creating stderr pipe for plugin. Please check the error log"
162+
}
163+
if err := cmd.Start(); err != nil {
164+
glog.Errorf("Error in starting plugin %q: error - %v", rule.Path, err)
165+
return cpmtypes.Unknown, "Error in starting plugin. Please check the error log"
166+
}
167+
168+
var (
169+
wg sync.WaitGroup
170+
stdout []byte
171+
stderr []byte
172+
stdoutErr error
173+
stderrErr error
174+
)
175+
176+
wg.Add(2)
177+
go func() {
178+
stdout, stdoutErr = readFromReader(stdoutPipe, maxCustomPluginBufferBytes)
179+
wg.Done()
180+
}()
181+
go func() {
182+
stderr, stderrErr = readFromReader(stderrPipe, maxCustomPluginBufferBytes)
183+
wg.Done()
184+
}()
185+
// This will wait for the reads to complete. If the execution times out, the pipes
186+
// will be closed and the wait group unblocks.
187+
wg.Wait()
188+
189+
if stdoutErr != nil {
190+
glog.Errorf("Error reading stdout for plugin %q: error - %v", rule.Path, err)
191+
return cpmtypes.Unknown, "Error reading stdout for plugin. Please check the error log"
192+
}
193+
194+
if stderrErr != nil {
195+
glog.Errorf("Error reading stderr for plugin %q: error - %v", rule.Path, err)
196+
return cpmtypes.Unknown, "Error reading stderr for plugin. Please check the error log"
197+
}
198+
199+
if err := cmd.Wait(); err != nil {
134200
if _, ok := err.(*exec.ExitError); !ok {
135-
glog.Errorf("Error in running plugin %q: error - %v. output - %q", rule.Path, err, string(stdout))
136-
return cpmtypes.Unknown, "Error in running plugin. Please check the error log"
201+
glog.Errorf("Error in waiting for plugin %q: error - %v. output - %q", rule.Path, err, string(stdout))
202+
return cpmtypes.Unknown, "Error in waiting for plugin. Please check the error log"
137203
}
138204
}
139205

206+
// log the stderr from the plugin
207+
if len(stderr) != 0 {
208+
glog.Infof("Start logs from plugin %q \n %s", rule.Path, string(stderr))
209+
glog.Infof("End logs from plugin %q", rule.Path)
210+
}
211+
140212
// trim suffix useless bytes
141213
output = string(stdout)
142214
output = strings.TrimSpace(output)

pkg/custompluginmonitor/plugin/plugin_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ func TestNewPluginRun(t *testing.T) {
6161
Timeout: &ruleTimeout,
6262
},
6363
ExitStatus: cpmtypes.Unknown,
64-
Output: "Error in running plugin. Please check the error log",
64+
Output: "Error in starting plugin. Please check the error log",
6565
},
6666
"longer than 80 stdout with ok exit status": {
6767
Rule: cpmtypes.CustomRule{

0 commit comments

Comments
 (0)