diff --git a/client/nginx.go b/client/nginx.go index bb884ba..8fe1084 100644 --- a/client/nginx.go +++ b/client/nginx.go @@ -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 } diff --git a/client/nginx_test.go b/client/nginx_test.go index 04a3bf6..b9e867c 100644 --- a/client/nginx_test.go +++ b/client/nginx_test.go @@ -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) } }