You've already forked nginx_exporter
mirror of
https://github.com/nginxinc/nginx-prometheus-exporter.git
synced 2025-07-30 10:03:04 +03:00
Simple parsing nginx metrics [parseStubStats] (#207)
This commit is contained in:
committed by
GitHub
parent
d14781a6bd
commit
c636d826c1
@ -1,14 +1,19 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const templateMetrics string = `Active connections: %d
|
||||
server accepts handled requests
|
||||
%d %d %d
|
||||
Reading: %d Writing: %d Waiting: %d
|
||||
`
|
||||
|
||||
// NginxClient allows you to fetch NGINX metrics from the stub_status page.
|
||||
type NginxClient struct {
|
||||
apiEndpoint string
|
||||
@ -66,79 +71,26 @@ func (client *NginxClient) GetStubStats() (*StubStats, error) {
|
||||
return nil, fmt.Errorf("failed to read the response body: %w", err)
|
||||
}
|
||||
|
||||
var stats StubStats
|
||||
err = parseStubStats(body, &stats)
|
||||
r := bytes.NewReader(body)
|
||||
stats, err := parseStubStats(r)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse response body %q: %w", string(body), err)
|
||||
}
|
||||
|
||||
return &stats, nil
|
||||
return stats, nil
|
||||
}
|
||||
|
||||
func parseStubStats(data []byte, stats *StubStats) error {
|
||||
dataStr := string(data)
|
||||
|
||||
parts := strings.Split(dataStr, "\n")
|
||||
if len(parts) != 5 {
|
||||
return fmt.Errorf("invalid input %q", dataStr)
|
||||
func parseStubStats(r io.Reader) (*StubStats, error) {
|
||||
var s StubStats
|
||||
if _, err := fmt.Fscanf(r, templateMetrics,
|
||||
&s.Connections.Active,
|
||||
&s.Connections.Accepted,
|
||||
&s.Connections.Handled,
|
||||
&s.Requests,
|
||||
&s.Connections.Reading,
|
||||
&s.Connections.Writing,
|
||||
&s.Connections.Waiting); err != nil {
|
||||
return nil, fmt.Errorf("failed to scan template metrics: %w", err)
|
||||
}
|
||||
|
||||
activeConsParts := strings.Split(strings.TrimSpace(parts[0]), " ")
|
||||
if len(activeConsParts) != 3 {
|
||||
return fmt.Errorf("invalid input for active connections %q", parts[0])
|
||||
}
|
||||
|
||||
actCons, err := strconv.ParseInt(activeConsParts[2], 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid input for active connections %q: %w", activeConsParts[2], err)
|
||||
}
|
||||
stats.Connections.Active = actCons
|
||||
|
||||
miscParts := strings.Split(strings.TrimSpace(parts[2]), " ")
|
||||
if len(miscParts) != 3 {
|
||||
return fmt.Errorf("invalid input for connections and requests %q", parts[2])
|
||||
}
|
||||
|
||||
acceptedCons, err := strconv.ParseInt(miscParts[0], 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid input for accepted connections %q: %w", miscParts[0], err)
|
||||
}
|
||||
stats.Connections.Accepted = acceptedCons
|
||||
|
||||
handledCons, err := strconv.ParseInt(miscParts[1], 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid input for handled connections %q: %w", miscParts[1], err)
|
||||
}
|
||||
stats.Connections.Handled = handledCons
|
||||
|
||||
requests, err := strconv.ParseInt(miscParts[2], 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid input for requests %q: %w", miscParts[2], err)
|
||||
}
|
||||
stats.Requests = requests
|
||||
|
||||
consParts := strings.Split(strings.TrimSpace(parts[3]), " ")
|
||||
if len(consParts) != 6 {
|
||||
return fmt.Errorf("invalid input for connections %q", parts[3])
|
||||
}
|
||||
|
||||
readingCons, err := strconv.ParseInt(consParts[1], 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid input for reading connections %q: %w", consParts[1], err)
|
||||
}
|
||||
stats.Connections.Reading = readingCons
|
||||
|
||||
writingCons, err := strconv.ParseInt(consParts[3], 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid input for writing connections %q: %w", consParts[3], err)
|
||||
}
|
||||
stats.Connections.Writing = writingCons
|
||||
|
||||
waitingCons, err := strconv.ParseInt(consParts[5], 10, 64)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid input for waiting connections %q: %w", consParts[5], err)
|
||||
}
|
||||
stats.Connections.Waiting = waitingCons
|
||||
|
||||
return nil
|
||||
return &s, nil
|
||||
}
|
||||
|
@ -1,6 +1,9 @@
|
||||
package client
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const validStabStats = "Active connections: 1457 \nserver accepts handled requests\n 6717066 6717066 65844359 \nReading: 1 Writing: 8 Waiting: 1448 \n"
|
||||
|
||||
@ -32,15 +35,14 @@ func TestParseStubStatsValidInput(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
var result StubStats
|
||||
|
||||
err := parseStubStats(test.input, &result)
|
||||
r := bytes.NewReader(test.input)
|
||||
result, err := parseStubStats(r)
|
||||
|
||||
if err != nil && !test.expectedError {
|
||||
t.Errorf("parseStubStats() returned error for valid input %q: %v", string(test.input), err)
|
||||
}
|
||||
|
||||
if !test.expectedError && test.expectedResult != result {
|
||||
if !test.expectedError && test.expectedResult != *result {
|
||||
t.Errorf("parseStubStats() result %v != expected %v for input %q", result, test.expectedResult, test.input)
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user