From 5523b934ae3f634a3af643625b24f6bb58e65b0b Mon Sep 17 00:00:00 2001 From: Aliaksandr Valialkin Date: Sun, 21 Feb 2021 22:45:08 +0200 Subject: [PATCH] add `process_io_* metrics read from `/proc/self/io` Metrics are: process_io_read_bytes_total - the number of bytes read via io syscalls such as read and pread process_io_written_bytes_total - the number of bytes written via io syscalls such as write and pwrite process_io_read_syscalls_total - the number of read syscalls such as read and pread process_io_write_syscalls_total - the number of write syscalls such as write and pwrite process_io_storage_read_bytes_total - the number of bytes read from storage layer process_io_storage_written_bytes_total - the number of bytes written to storage layer --- process_metrics_linux.go | 54 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/process_metrics_linux.go b/process_metrics_linux.go index f04a503..895ca72 100644 --- a/process_metrics_linux.go +++ b/process_metrics_linux.go @@ -12,8 +12,6 @@ import ( "time" ) -const statFilepath = "/proc/self/stat" - // See https://github.com/prometheus/procfs/blob/a4ac0826abceb44c40fc71daed2b301db498b93e/proc_stat.go#L40 . const userHZ = 100 @@ -44,6 +42,7 @@ type procStat struct { } func writeProcessMetrics(w io.Writer) { + statFilepath := "/proc/self/stat" data, err := ioutil.ReadFile(statFilepath) if err != nil { log.Printf("ERROR: cannot open %s: %s", statFilepath, err) @@ -68,7 +67,8 @@ func writeProcessMetrics(w io.Writer) { } // It is expensive obtaining `process_open_fds` when big number of file descriptors is opened, - // don't do it here. + // so don't do it here. + // See writeFDMetrics instead. utime := float64(p.Utime) / userHZ stime := float64(p.Stime) / userHZ @@ -81,6 +81,54 @@ func writeProcessMetrics(w io.Writer) { fmt.Fprintf(w, "process_resident_memory_bytes %d\n", p.Rss*4096) fmt.Fprintf(w, "process_start_time_seconds %d\n", startTimeSeconds) fmt.Fprintf(w, "process_virtual_memory_bytes %d\n", p.Vsize) + + writeIOMetrics(w) +} + +func writeIOMetrics(w io.Writer) { + ioFilepath := "/proc/self/io" + data, err := ioutil.ReadFile(ioFilepath) + if err != nil { + log.Printf("ERROR: cannot open %q: %s", ioFilepath, err) + } + getInt := func(s string) int64 { + n := strings.IndexByte(s, ' ') + if n < 0 { + log.Printf("ERROR: cannot find whitespace in %q at %q", s, ioFilepath) + return 0 + } + v, err := strconv.ParseInt(s[n+1:], 10, 64) + if err != nil { + log.Printf("ERROR: cannot parse %q at %q: %s", s, ioFilepath, err) + return 0 + } + return v + } + var rchar, wchar, syscr, syscw, readBytes, writeBytes int64 + lines := strings.Split(string(data), "\n") + for _, s := range lines { + s = strings.TrimSpace(s) + switch { + case strings.HasPrefix(s, "rchar: "): + rchar = getInt(s) + case strings.HasPrefix(s, "wchar: "): + wchar = getInt(s) + case strings.HasPrefix(s, "syscr: "): + syscr = getInt(s) + case strings.HasPrefix(s, "syscw: "): + syscw = getInt(s) + case strings.HasPrefix(s, "read_bytes: "): + readBytes = getInt(s) + case strings.HasPrefix(s, "write_bytes: "): + writeBytes = getInt(s) + } + } + fmt.Fprintf(w, "process_io_read_bytes_total %d\n", rchar) + fmt.Fprintf(w, "process_io_written_bytes_total %d\n", wchar) + fmt.Fprintf(w, "process_io_read_syscalls_total %d\n", syscr) + fmt.Fprintf(w, "process_io_write_syscalls_total %d\n", syscw) + fmt.Fprintf(w, "process_io_storage_read_bytes_total %d\n", readBytes) + fmt.Fprintf(w, "process_io_storage_written_bytes_total %d\n", writeBytes) } var startTimeSeconds = time.Now().Unix()