exec plugins should not truncate messages in debug mode (#8333)
This commit is contained in:
parent
30830c2ec2
commit
4dcc3c0ad7
|
|
@ -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"
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
@ -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{})
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package exec
|
|||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
|
|
@ -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
|
||||
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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())
|
||||
})
|
||||
}
|
||||
|
|
@ -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()))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import (
|
|||
"io"
|
||||
"log"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
|
|
@ -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
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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 {
|
||||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue