This commit is contained in:
Aliaksandr Valialkin 2021-02-04 16:28:55 +02:00
parent 2a12f948a9
commit c070763356
4 changed files with 56 additions and 52 deletions

View File

@ -58,11 +58,17 @@ func WritePrometheus(w io.Writer, exposeProcessMetrics bool) {
// metrics.WriteProcessMetrics(w) // metrics.WriteProcessMetrics(w)
// }) // })
// //
// See also WrteFDMetrics.
func WriteProcessMetrics(w io.Writer) { func WriteProcessMetrics(w io.Writer) {
writeGoMetrics(w) writeGoMetrics(w)
writeProcessMetrics(w) writeProcessMetrics(w)
} }
// WriteFDMetrics writes `process_max_fds` and `process_open_fds` metrics to w.
func WriteFDMetrics(w io.Writer) {
writeFDMetrics(w)
}
// UnregisterMetric removes metric with the given name from default set. // UnregisterMetric removes metric with the given name from default set.
func UnregisterMetric(name string) bool { func UnregisterMetric(name string) bool {
return defaultSet.UnregisterMetric(name) return defaultSet.UnregisterMetric(name)

View File

@ -8,7 +8,6 @@ import (
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
"regexp"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -87,74 +86,70 @@ func writeProcessMetrics(w io.Writer) {
var startTimeSeconds = time.Now().Unix() var startTimeSeconds = time.Now().Unix()
const fdReadChunkSize = 512 // WriteFDMetrics writes process_max_fds and process_open_fds metrics to w.
func writeFDMetrics(w io.Writer) {
func WriteFDMetrics(w io.Writer){
totalOpenFDs, err := getOpenFDsCount("/proc/self/fd") totalOpenFDs, err := getOpenFDsCount("/proc/self/fd")
if err != nil { if err != nil {
log.Printf("ERROR: %v",err) log.Printf("ERROR: cannot determine open file descriptors count: %s", err)
return return
} }
maxOpenFDs, err := getMaxFilesLimit("/proc/self/limits") maxOpenFDs, err := getMaxFilesLimit("/proc/self/limits")
if err != nil { if err != nil {
log.Printf("ERROR: %v",err) log.Printf("ERROR: cannot determine the limit on open file descritors: %s", err)
return return
} }
fmt.Fprintf(w,"process_max_fds %d\n",maxOpenFDs) fmt.Fprintf(w, "process_max_fds %d\n", maxOpenFDs)
fmt.Fprintf(w,"process_open_fds %d\n",totalOpenFDs) fmt.Fprintf(w, "process_open_fds %d\n", totalOpenFDs)
} }
func getOpenFDsCount(path string) (uint64, error) {
func getOpenFDsCount(path string)(uint64,error){
f, err := os.Open(path) f, err := os.Open(path)
if err != nil { if err != nil {
return 0,fmt.Errorf("cannot open process fd path: %q, err: %v",path,err) return 0, err
} }
defer f.Close() defer f.Close()
var totalOpenFDs uint64 var totalOpenFDs uint64
for { for {
names, err := f.Readdirnames(fdReadChunkSize) names, err := f.Readdirnames(512)
if err == io.EOF{ if err == io.EOF {
break break
} }
if err != nil { if err != nil {
return 0, fmt.Errorf("unexpected error at readdirnames: %v",err) return 0, fmt.Errorf("unexpected error at Readdirnames: %s", err)
} }
totalOpenFDs += uint64(len(names)) totalOpenFDs += uint64(len(names))
} }
return totalOpenFDs, nil return totalOpenFDs, nil
} }
var limitsRe = regexp.MustCompile(`(Max \w+\s{0,1}?\w*\s{0,1}\w*)\s{2,}(\w+)\s+(\w+)`) func getMaxFilesLimit(path string) (uint64, error) {
func getMaxFilesLimit(path string)(uint64,error){
f, err := os.Open(path) f, err := os.Open(path)
if err != nil { if err != nil {
return 0,fmt.Errorf("cannot open path: %q for max files limit, err: %w",path,err) return 0, err
} }
defer f.Close() defer f.Close()
prefix := "Max open files"
scan := bufio.NewScanner(f) scan := bufio.NewScanner(f)
// skip first line for scan.Scan() {
scan.Scan() s := scan.Text()
for scan.Scan(){ if !strings.HasPrefix(s, prefix) {
text := scan.Text()
if !strings.HasPrefix(text,"Max open files"){
continue continue
} }
items := limitsRe.FindStringSubmatch(text) text := strings.TrimSpace(s[len(prefix):])
if len(items) != 4 { // Extract soft limit.
return 0,fmt.Errorf("unxpected fields num for limits file, want: %d, got: %d, line: %q",4,len(items),text) n := strings.IndexByte(text, ' ')
if n < 0 {
return 0, fmt.Errorf("cannot extract soft limit from %q", s)
} }
// use soft limit. text = text[:n]
limit := items[2] if text == "unlimited" {
if limit == "unlimited"{ return 1<<64 - 1, nil
return 18446744073709551615,nil
} }
limitUint, err := strconv.ParseUint(limit,10,64) limit, err := strconv.ParseUint(text, 10, 64)
if err != nil { if err != nil {
return 0, fmt.Errorf("cannot parse limit: %q as uint64: %w",limit,err) return 0, fmt.Errorf("cannot parse soft limit from %q: %s", s, err)
} }
return limitUint,nil return limit, nil
} }
return 0, fmt.Errorf("max open files limit wasn't found") return 0, fmt.Errorf("cannot find max open files limit")
} }

View File

@ -2,36 +2,35 @@ package metrics
import "testing" import "testing"
func TestGetMaxFilesLimit(t *testing.T){ func TestGetMaxFilesLimit(t *testing.T) {
f := func(want uint64,path string, wantErr bool) { f := func(want uint64, path string, wantErr bool) {
t.Helper() t.Helper()
got, err := getMaxFilesLimit(path) got, err := getMaxFilesLimit(path)
if err != nil && !wantErr { if err != nil && !wantErr {
t.Fatalf("unexpected error: %v",err) t.Fatalf("unexpected error: %v", err)
} }
if got != want{ if got != want {
t.Fatalf("unexpected result: %d, want: %d at getMaxFilesLimit",got,want) t.Fatalf("unexpected result: %d, want: %d at getMaxFilesLimit", got, want)
} }
} }
f(1024,"testdata/limits",false) f(1024, "testdata/limits", false)
f(0,"testdata/bad_path",true) f(0, "testdata/bad_path", true)
f(0,"testdata/limits_bad",true) f(0, "testdata/limits_bad", true)
} }
func TestGetOpenFDsCount(t *testing.T) {
func TestGetOpenFDsCount(t *testing.T){ f := func(want uint64, path string, wantErr bool) {
f := func(want uint64,path string, wantErr bool) {
t.Helper() t.Helper()
got, err := getOpenFDsCount(path) got, err := getOpenFDsCount(path)
if (err != nil && !wantErr)|| (err == nil && wantErr) { if (err != nil && !wantErr) || (err == nil && wantErr) {
t.Fatalf("unexpected error: %v",err) t.Fatalf("unexpected error: %v", err)
} }
if got != want{ if got != want {
t.Fatalf("unexpected result: %d, want: %d at getOpenFDsCount",got,want) t.Fatalf("unexpected result: %d, want: %d at getOpenFDsCount", got, want)
} }
} }
f(5,"testdata/fd/",false) f(5, "testdata/fd/", false)
f(0,"testdata/fd/0",true) f(0, "testdata/fd/0", true)
f(0,"testdata/limits",true) f(0, "testdata/limits", true)
} }

View File

@ -9,3 +9,7 @@ import (
func writeProcessMetrics(w io.Writer) { func writeProcessMetrics(w io.Writer) {
// TODO: implement it // TODO: implement it
} }
func writeFDMetrics(w io.Writer) {
// TODO: implement it.
}