diff --git a/lib/logger/logger.go b/lib/logger/logger.go index 75ca9a7f2..26d6e1ea6 100644 --- a/lib/logger/logger.go +++ b/lib/logger/logger.go @@ -70,7 +70,7 @@ type logger struct { var DefaultLogger = New() func New() Logger { - return newLogger(os.Stdout) + return newLogger(controlStripper{os.Stdout}) } func newLogger(w io.Writer) Logger { @@ -376,3 +376,23 @@ func (r *recorder) append(l LogLevel, msg string) { r.lines = append(r.lines, Line{time.Now(), "...", l}) } } + +// controlStripper is a Writer that replaces control characters +// with spaces. +type controlStripper struct { + io.Writer +} + +func (s controlStripper) Write(data []byte) (int, error) { + for i, b := range data { + if b == '\n' || b == '\r' { + // Newlines are OK + continue + } + if b < 32 { + // Characters below 32 are control characters + data[i] = ' ' + } + } + return s.Writer.Write(data) +} diff --git a/lib/logger/logger_test.go b/lib/logger/logger_test.go index 9e0db1285..1937aa646 100644 --- a/lib/logger/logger_test.go +++ b/lib/logger/logger_test.go @@ -6,6 +6,7 @@ package logger import ( "bytes" "fmt" + "io/ioutil" "log" "strings" "testing" @@ -166,3 +167,43 @@ func TestStackLevel(t *testing.T) { t.Error("Should identify this file as the source (bad level?)") } } + +func TestControlStripper(t *testing.T) { + b := new(bytes.Buffer) + l := newLogger(controlStripper{b}) + + l.Infoln("testing\x07testing\ntesting") + res := b.String() + + if !strings.Contains(res, "testing testing\ntesting") { + t.Logf("%q", res) + t.Error("Control character should become space") + } + if strings.Contains(res, "\x07") { + t.Logf("%q", res) + t.Error("Control character should be removed") + } +} + +func BenchmarkLog(b *testing.B) { + l := newLogger(controlStripper{ioutil.Discard}) + benchmarkLogger(b, l) +} + +func BenchmarkLogNoStripper(b *testing.B) { + l := newLogger(ioutil.Discard) + benchmarkLogger(b, l) +} + +func benchmarkLogger(b *testing.B, l Logger) { + l.SetFlags(log.Lshortfile | log.Lmicroseconds) + l.SetPrefix("ABCDEFG") + + for i := 0; i < b.N; i++ { + l.Infoln("This is a somewhat representative log line") + l.Infof("This is a log line with a couple of formatted things: %d %q", 42, "a file name maybe, who knows?") + } + + b.ReportAllocs() + b.SetBytes(2) // log entries per iteration +}