diff --git a/cmd/derper/derper.go b/cmd/derper/derper.go index 07653141d..2974d3f25 100644 --- a/cmd/derper/derper.go +++ b/cmd/derper/derper.go @@ -26,7 +26,6 @@ import ( "syscall" "time" - "go4.org/mem" "golang.org/x/time/rate" "tailscale.com/atomicfile" "tailscale.com/derp" @@ -36,6 +35,7 @@ import ( "tailscale.com/net/stunserver" "tailscale.com/tsweb" "tailscale.com/types/key" + "tailscale.com/types/logger" ) var ( @@ -235,7 +235,7 @@ func main() { KeepAlive: *tcpKeepAlive, } - quietLogger := log.New(logFilter{}, "", 0) + quietLogger := log.New(logger.HTTPServerLogFilter{Inner: log.Printf}, "", 0) httpsrv := &http.Server{ Addr: *addr, Handler: mux, @@ -452,22 +452,3 @@ func (l *rateLimitedListener) Accept() (net.Conn, error) { l.numAccepts.Add(1) return cn, nil } - -// logFilter is used to filter out useless error logs that are logged to -// the net/http.Server.ErrorLog logger. -type logFilter struct{} - -func (logFilter) Write(p []byte) (int, error) { - b := mem.B(p) - if mem.HasSuffix(b, mem.S(": EOF\n")) || - mem.HasSuffix(b, mem.S(": i/o timeout\n")) || - mem.HasSuffix(b, mem.S(": read: connection reset by peer\n")) || - mem.HasSuffix(b, mem.S(": remote error: tls: bad certificate\n")) || - mem.HasSuffix(b, mem.S(": tls: first record does not look like a TLS handshake\n")) { - // Skip this log message, but say that we processed it - return len(p), nil - } - - log.Printf("%s", p) - return len(p), nil -} diff --git a/types/logger/logger.go b/types/logger/logger.go index a2f8e0256..11596b357 100644 --- a/types/logger/logger.go +++ b/types/logger/logger.go @@ -20,6 +20,7 @@ import ( "context" + "go4.org/mem" "tailscale.com/envknob" "tailscale.com/util/ctxkey" ) @@ -393,3 +394,25 @@ func TestLogger(tb TBLogger) Logf { tb.Logf(" ... "+format, args...) } } + +// HTTPServerLogFilter is an io.Writer that can be used as the +// net/http.Server.ErrorLog logger, and will filter out noisy, low-signal +// messages that clutter up logs. +type HTTPServerLogFilter struct { + Inner Logf +} + +func (lf HTTPServerLogFilter) Write(p []byte) (int, error) { + b := mem.B(p) + if mem.HasSuffix(b, mem.S(": EOF\n")) || + mem.HasSuffix(b, mem.S(": i/o timeout\n")) || + mem.HasSuffix(b, mem.S(": read: connection reset by peer\n")) || + mem.HasSuffix(b, mem.S(": remote error: tls: bad certificate\n")) || + mem.HasSuffix(b, mem.S(": tls: first record does not look like a TLS handshake\n")) { + // Skip this log message, but say that we processed it + return len(p), nil + } + + lf.Inner("%s", p) + return len(p), nil +} diff --git a/types/logger/logger_test.go b/types/logger/logger_test.go index e91977a8f..2d06ab851 100644 --- a/types/logger/logger_test.go +++ b/types/logger/logger_test.go @@ -258,3 +258,23 @@ func TestAsJSON(t *testing.T) { t.Errorf("allocs = %v; want max 2", n) } } + +func TestHTTPServerLogFilter(t *testing.T) { + var buf bytes.Buffer + logf := func(format string, args ...any) { + t.Logf("[logf] "+format, args...) + fmt.Fprintf(&buf, format, args...) + } + + lf := HTTPServerLogFilter{logf} + quietLogger := log.New(lf, "", 0) + + quietLogger.Printf("foo bar") + quietLogger.Printf("http: TLS handshake error from %s:%d: EOF", "1.2.3.4", 9999) + quietLogger.Printf("baz") + + const want = "foo bar\nbaz\n" + if s := buf.String(); s != want { + t.Errorf("got buf=%q, want %q", s, want) + } +}