adds file descriptors metrics, (#19)
* adds file descriptors metrics, for linux based systems, https://github.com/VictoriaMetrics/VictoriaMetrics/issues/1037 * small fix
This commit is contained in:
parent
be819551e3
commit
2a12f948a9
@ -1,11 +1,16 @@
|
||||
package metrics
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -81,3 +86,75 @@ func writeProcessMetrics(w io.Writer) {
|
||||
}
|
||||
|
||||
var startTimeSeconds = time.Now().Unix()
|
||||
|
||||
const fdReadChunkSize = 512
|
||||
|
||||
func WriteFDMetrics(w io.Writer){
|
||||
totalOpenFDs, err := getOpenFDsCount("/proc/self/fd")
|
||||
if err != nil {
|
||||
log.Printf("ERROR: %v",err)
|
||||
return
|
||||
}
|
||||
maxOpenFDs, err := getMaxFilesLimit("/proc/self/limits")
|
||||
if err != nil {
|
||||
log.Printf("ERROR: %v",err)
|
||||
return
|
||||
}
|
||||
fmt.Fprintf(w,"process_max_fds %d\n",maxOpenFDs)
|
||||
fmt.Fprintf(w,"process_open_fds %d\n",totalOpenFDs)
|
||||
}
|
||||
|
||||
|
||||
func getOpenFDsCount(path string)(uint64,error){
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return 0,fmt.Errorf("cannot open process fd path: %q, err: %v",path,err)
|
||||
}
|
||||
defer f.Close()
|
||||
var totalOpenFDs uint64
|
||||
for {
|
||||
names, err := f.Readdirnames(fdReadChunkSize)
|
||||
if err == io.EOF{
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("unexpected error at readdirnames: %v",err)
|
||||
}
|
||||
totalOpenFDs += uint64(len(names))
|
||||
}
|
||||
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){
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return 0,fmt.Errorf("cannot open path: %q for max files limit, err: %w",path,err)
|
||||
}
|
||||
defer f.Close()
|
||||
scan := bufio.NewScanner(f)
|
||||
// skip first line
|
||||
scan.Scan()
|
||||
for scan.Scan(){
|
||||
text := scan.Text()
|
||||
if !strings.HasPrefix(text,"Max open files"){
|
||||
continue
|
||||
}
|
||||
items := limitsRe.FindStringSubmatch(text)
|
||||
if len(items) != 4 {
|
||||
return 0,fmt.Errorf("unxpected fields num for limits file, want: %d, got: %d, line: %q",4,len(items),text)
|
||||
}
|
||||
// use soft limit.
|
||||
limit := items[2]
|
||||
if limit == "unlimited"{
|
||||
return 18446744073709551615,nil
|
||||
}
|
||||
limitUint, err := strconv.ParseUint(limit,10,64)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("cannot parse limit: %q as uint64: %w",limit,err)
|
||||
}
|
||||
return limitUint,nil
|
||||
}
|
||||
return 0, fmt.Errorf("max open files limit wasn't found")
|
||||
}
|
||||
|
37
process_metrics_linux_test.go
Normal file
37
process_metrics_linux_test.go
Normal file
@ -0,0 +1,37 @@
|
||||
package metrics
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestGetMaxFilesLimit(t *testing.T){
|
||||
f := func(want uint64,path string, wantErr bool) {
|
||||
t.Helper()
|
||||
got, err := getMaxFilesLimit(path)
|
||||
if err != nil && !wantErr {
|
||||
t.Fatalf("unexpected error: %v",err)
|
||||
}
|
||||
if got != want{
|
||||
t.Fatalf("unexpected result: %d, want: %d at getMaxFilesLimit",got,want)
|
||||
}
|
||||
|
||||
}
|
||||
f(1024,"testdata/limits",false)
|
||||
f(0,"testdata/bad_path",true)
|
||||
f(0,"testdata/limits_bad",true)
|
||||
}
|
||||
|
||||
|
||||
func TestGetOpenFDsCount(t *testing.T){
|
||||
f := func(want uint64,path string, wantErr bool) {
|
||||
t.Helper()
|
||||
got, err := getOpenFDsCount(path)
|
||||
if (err != nil && !wantErr)|| (err == nil && wantErr) {
|
||||
t.Fatalf("unexpected error: %v",err)
|
||||
}
|
||||
if got != want{
|
||||
t.Fatalf("unexpected result: %d, want: %d at getOpenFDsCount",got,want)
|
||||
}
|
||||
}
|
||||
f(5,"testdata/fd/",false)
|
||||
f(0,"testdata/fd/0",true)
|
||||
f(0,"testdata/limits",true)
|
||||
}
|
0
testdata/fd/0
vendored
Normal file
0
testdata/fd/0
vendored
Normal file
0
testdata/fd/10
vendored
Normal file
0
testdata/fd/10
vendored
Normal file
0
testdata/fd/2
vendored
Normal file
0
testdata/fd/2
vendored
Normal file
0
testdata/fd/3
vendored
Normal file
0
testdata/fd/3
vendored
Normal file
0
testdata/fd/5
vendored
Normal file
0
testdata/fd/5
vendored
Normal file
17
testdata/limits
vendored
Normal file
17
testdata/limits
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
Limit Soft Limit Hard Limit Units
|
||||
Max cpu time unlimited unlimited seconds
|
||||
Max file size unlimited unlimited bytes
|
||||
Max data size unlimited unlimited bytes
|
||||
Max stack size 8388608 unlimited bytes
|
||||
Max core file size 0 unlimited bytes
|
||||
Max resident set unlimited unlimited bytes
|
||||
Max processes 127458 127458 processes
|
||||
Max open files 1024 1048576 files
|
||||
Max locked memory 67108864 67108864 bytes
|
||||
Max address space unlimited unlimited bytes
|
||||
Max file locks unlimited unlimited locks
|
||||
Max pending signals 127458 127458 signals
|
||||
Max msgqueue size 819200 819200 bytes
|
||||
Max nice priority 0 0
|
||||
Max realtime priority 0 0
|
||||
Max realtime timeout unlimited unlimited us
|
1
testdata/limits_bad
vendored
Normal file
1
testdata/limits_bad
vendored
Normal file
@ -0,0 +1 @@
|
||||
Limit Soft Limit Hard Limit Units
|
Loading…
Reference in New Issue
Block a user