1
0
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:
Andrei Grazhdankov
2021-07-21 23:49:29 +03:00
committed by GitHub
parent d14781a6bd
commit c636d826c1
2 changed files with 29 additions and 75 deletions

View File

@ -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
}

View File

@ -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)
}
}