Skip to content

Commit

Permalink
exec plugins should not truncate messages in debug mode (influxdata#8333
Browse files Browse the repository at this point in the history
)
  • Loading branch information
ssoroka authored Mar 18, 2021
1 parent 30830c2 commit 4dcc3c0
Show file tree
Hide file tree
Showing 7 changed files with 63 additions and 31 deletions.
4 changes: 3 additions & 1 deletion cmd/telegraf/telegraf.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"syscall"
"time"

"github.com/influxdata/telegraf"
"github.com/influxdata/telegraf/agent"
"github.com/influxdata/telegraf/config"
"github.com/influxdata/telegraf/internal"
Expand Down Expand Up @@ -158,8 +159,9 @@ func runAgent(ctx context.Context,
}

// Setup logging as configured.
telegraf.Debug = ag.Config.Agent.Debug || *fDebug
logConfig := logger.LogConfig{
Debug: ag.Config.Agent.Debug || *fDebug,
Debug: telegraf.Debug,
Quiet: ag.Config.Agent.Quiet || *fQuiet,
LogTarget: ag.Config.Agent.LogTarget,
Logfile: ag.Config.Agent.Logfile,
Expand Down
4 changes: 3 additions & 1 deletion plugin.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package telegraf

var Debug bool

// Initializer is an interface that all plugin types: Inputs, Outputs,
// Processors, and Aggregators can optionally implement to initialize the
// plugin.
Expand All @@ -21,7 +23,7 @@ type PluginDescriber interface {
Description() string
}

// Logger defines an interface for logging.
// Logger defines an plugin-related interface for logging.
type Logger interface {
// Errorf logs an error message, patterned after log.Printf.
Errorf(format string, args ...interface{})
Expand Down
41 changes: 18 additions & 23 deletions plugins/inputs/exec/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package exec
import (
"bytes"
"fmt"
"io"
"os/exec"
"path/filepath"
"runtime"
Expand Down Expand Up @@ -39,12 +40,12 @@ const sampleConfig = `
data_format = "influx"
`

const MaxStderrBytes = 512
const MaxStderrBytes int = 512

type Exec struct {
Commands []string
Command string
Timeout internal.Duration
Commands []string `toml:"commands"`
Command string `toml:"command"`
Timeout internal.Duration `toml:"timeout"`

parser parsers.Parser

Expand Down Expand Up @@ -85,16 +86,16 @@ func (c CommandRunner) Run(

runErr := internal.RunTimeout(cmd, timeout)

out = removeCarriageReturns(out)
if stderr.Len() > 0 {
stderr = removeCarriageReturns(stderr)
stderr = truncate(stderr)
out = removeWindowsCarriageReturns(out)
if stderr.Len() > 0 && !telegraf.Debug {
stderr = removeWindowsCarriageReturns(stderr)
stderr = c.truncate(stderr)
}

return out.Bytes(), stderr.Bytes(), runErr
}

func truncate(buf bytes.Buffer) bytes.Buffer {
func (c CommandRunner) truncate(buf bytes.Buffer) bytes.Buffer {
// Limit the number of bytes.
didTruncate := false
if buf.Len() > MaxStderrBytes {
Expand All @@ -114,27 +115,21 @@ func truncate(buf bytes.Buffer) bytes.Buffer {
return buf
}

// removeCarriageReturns removes all carriage returns from the input if the
// removeWindowsCarriageReturns removes all carriage returns from the input if the
// OS is Windows. It does not return any errors.
func removeCarriageReturns(b bytes.Buffer) bytes.Buffer {
func removeWindowsCarriageReturns(b bytes.Buffer) bytes.Buffer {
if runtime.GOOS == "windows" {
var buf bytes.Buffer
for {
byt, er := b.ReadBytes(0x0D)
end := len(byt)
if nil == er {
end--
byt, err := b.ReadBytes(0x0D)
byt = bytes.TrimRight(byt, "\x0d")
if len(byt) > 0 {
_, _ = buf.Write(byt)
}
if nil != byt {
buf.Write(byt[:end])
} else {
break
}
if nil != er {
break
if err == io.EOF {
return buf
}
}
b = buf
}
return b
}
Expand Down
7 changes: 4 additions & 3 deletions plugins/inputs/exec/exec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -259,9 +259,10 @@ func TestTruncate(t *testing.T) {
},
}

c := CommandRunner{}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
res := truncate(*tt.bufF())
res := c.truncate(*tt.bufF())
require.Equal(t, tt.expF().Bytes(), res.Bytes())
})
}
Expand All @@ -272,14 +273,14 @@ func TestRemoveCarriageReturns(t *testing.T) {
// Test that all carriage returns are removed
for _, test := range crTests {
b := bytes.NewBuffer(test.input)
out := removeCarriageReturns(*b)
out := removeWindowsCarriageReturns(*b)
assert.True(t, bytes.Equal(test.output, out.Bytes()))
}
} else {
// Test that the buffer is returned unaltered
for _, test := range crTests {
b := bytes.NewBuffer(test.input)
out := removeCarriageReturns(*b)
out := removeWindowsCarriageReturns(*b)
assert.True(t, bytes.Equal(test.input, out.Bytes()))
}
}
Expand Down
2 changes: 2 additions & 0 deletions plugins/outputs/exec/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ The command should be defined similar to docker's `exec` form:

On non-zero exit stderr will be logged at error level.

For better performance, consider execd, which runs continuously.

### Configuration

```toml
Expand Down
33 changes: 31 additions & 2 deletions plugins/outputs/exec/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"io"
"log"
"os/exec"
"runtime"
"time"

"github.com/influxdata/telegraf"
Expand Down Expand Up @@ -39,6 +40,10 @@ var sampleConfig = `
# data_format = "influx"
`

func (e *Exec) Init() error {
return nil
}

// SetSerializer sets the serializer for the output.
func (e *Exec) SetSerializer(serializer serializers.Serializer) {
e.serializer = serializer
Expand Down Expand Up @@ -105,8 +110,13 @@ func (c *CommandRunner) Run(timeout time.Duration, command []string, buffer io.R
return fmt.Errorf("%q timed out and was killed", command)
}

s = removeWindowsCarriageReturns(s)
if s.Len() > 0 {
log.Printf("E! [outputs.exec] Command error: %q", truncate(s))
if !telegraf.Debug {
log.Printf("E! [outputs.exec] Command error: %q", c.truncate(s))
} else {
log.Printf("D! [outputs.exec] Command error: %q", s)
}
}

if status, ok := internal.ExitStatus(err); ok {
Expand All @@ -121,7 +131,7 @@ func (c *CommandRunner) Run(timeout time.Duration, command []string, buffer io.R
return nil
}

func truncate(buf bytes.Buffer) string {
func (c *CommandRunner) truncate(buf bytes.Buffer) string {
// Limit the number of bytes.
didTruncate := false
if buf.Len() > maxStderrBytes {
Expand Down Expand Up @@ -149,3 +159,22 @@ func init() {
}
})
}

// removeWindowsCarriageReturns removes all carriage returns from the input if the
// OS is Windows. It does not return any errors.
func removeWindowsCarriageReturns(b bytes.Buffer) bytes.Buffer {
if runtime.GOOS == "windows" {
var buf bytes.Buffer
for {
byt, err := b.ReadBytes(0x0D)
byt = bytes.TrimRight(byt, "\x0d")
if len(byt) > 0 {
_, _ = buf.Write(byt)
}
if err == io.EOF {
return buf
}
}
}
return b
}
3 changes: 2 additions & 1 deletion plugins/outputs/exec/exec_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,10 @@ func TestTruncate(t *testing.T) {
len: len("hola") + len("..."),
},
}
c := CommandRunner{}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := truncate(*tt.buf)
s := c.truncate(*tt.buf)
require.Equal(t, tt.len, len(s))
})
}
Expand Down

0 comments on commit 4dcc3c0

Please sign in to comment.