From 956a1cedc6269b40a67cb32bc1ffdd6aa8c60195 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ga=C3=A1l=20Gy=C3=B6rgy?= Date: Mon, 6 Jul 2026 22:06:35 +0200 Subject: [PATCH] Do not log client scrape disconnects as errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a scraper (e.g. Prometheus) closes the connection before the /metrics response is fully written, promhttp calls the configured ErrorLog once per metric family, flooding the log with "error encoding and sending metric family: ... write: broken pipe" lines for a single benign event (a scrape timeout or the scraper restarting). Detect EPIPE/ECONNRESET in the promhttp error logger and log those at V(1) instead of the error stream. Genuine encoding/gathering errors are still logged as errors. Signed-off-by: Gaál György --- main.go | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/main.go b/main.go index 406e394..dbe92a6 100644 --- a/main.go +++ b/main.go @@ -3,6 +3,7 @@ package main import ( "bytes" "context" + "errors" "net/http" _ "net/http/pprof" "os" @@ -220,9 +221,24 @@ func info(name, version string) prometheus.Collector { type logger struct{} func (l logger) Println(v ...interface{}) { + for _, arg := range v { + if err, ok := arg.(error); ok && isClientDisconnect(err) { + // A scraper (e.g. Prometheus) closed the connection before the + // /metrics response was fully written. This is benign - usually a + // scrape timeout or the scraper restarting - and promhttp reports it + // once per metric family, so keep it out of the error stream to avoid + // log spam. + klog.V(1).Infoln(v...) + return + } + } klog.Errorln(v...) } +func isClientDisconnect(err error) bool { + return errors.Is(err, syscall.EPIPE) || errors.Is(err, syscall.ECONNRESET) +} + type RateLimitedLogOutput struct { limiter *rate.Limiter }