master
go 147 lines 3.22 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package nginx
4
5 import (
6 "bufio"
7 "fmt"
8 "io"
9 "regexp"
10 "strconv"
11 "strings"
12 )
13
14 const (
15 connActive = "connActive"
16 connAccepts = "connAccepts"
17 connHandled = "connHandled"
18 requests = "requests"
19 requestTime = "requestTime"
20 connReading = "connReading"
21 connWriting = "connWriting"
22 connWaiting = "connWaiting"
23 )
24
25 var (
26 nginxSeq = []string{
27 connActive,
28 connAccepts,
29 connHandled,
30 requests,
31 connReading,
32 connWriting,
33 connWaiting,
34 }
35 tengineSeq = []string{
36 connActive,
37 connAccepts,
38 connHandled,
39 requests,
40 requestTime,
41 connReading,
42 connWriting,
43 connWaiting,
44 }
45
46 reStatus = regexp.MustCompile(`^Active connections: ([0-9]+)\n[^\d]+([0-9]+) ([0-9]+) ([0-9]+) ?([0-9]+)?\nReading: ([0-9]+) Writing: ([0-9]+) Waiting: ([0-9]+)`)
47 )
48
49 type stubStatus struct {
50 Connections struct {
51 // The current number of active client connections including Waiting connections.
52 Active int64 `stm:"active"`
53
54 // The total number of accepted client connections.
55 Accepts int64 `stm:"accepts"`
56
57 // The total number of handled connections.
58 // Generally, the parameter value is the same as accepts unless some resource limits have been reached.
59 Handled int64 `stm:"handled"`
60
61 // The current number of connections where nginx is reading the request header.
62 Reading int64 `stm:"reading"`
63
64 // The current number of connections where nginx is writing the response back to the client.
65 Writing int64 `stm:"writing"`
66
67 // The current number of idle client connections waiting for a request.
68 Waiting int64 `stm:"waiting"`
69 } `stm:""`
70 Requests struct {
71 // The total number of client requests.
72 Total int64 `stm:"requests"`
73
74 // Note: tengine specific
75 // The total requests' response time, which is in millisecond
76 Time *int64 `stm:"request_time"`
77 } `stm:""`
78 }
79
80 func parseStubStatus(r io.Reader) (*stubStatus, error) {
81 sc := bufio.NewScanner(r)
82 var lines []string
83
84 for sc.Scan() {
85 lines = append(lines, strings.Trim(sc.Text(), "\r\n "))
86 }
87
88 parsed := reStatus.FindStringSubmatch(strings.Join(lines, "\n"))
89
90 if len(parsed) == 0 {
91 return nil, fmt.Errorf("can't parse '%v'", lines)
92 }
93
94 parsed = parsed[1:]
95
96 var (
97 seq []string
98 status stubStatus
99 )
100
101 switch len(parsed) {
102 default:
103 return nil, fmt.Errorf("invalid number of fields, got %d, expect %d or %d", len(parsed), len(nginxSeq), len(tengineSeq))
104 case len(nginxSeq):
105 seq = nginxSeq
106 case len(tengineSeq):
107 seq = tengineSeq
108 }
109
110 for i, key := range seq {
111 strValue := parsed[i]
112 if strValue == "" {
113 continue
114 }
115 value := mustParseInt(strValue)
116 switch key {
117 default:
118 return nil, fmt.Errorf("unknown key in seq : %s", key)
119 case connActive:
120 status.Connections.Active = value
121 case connAccepts:
122 status.Connections.Accepts = value
123 case connHandled:
124 status.Connections.Handled = value
125 case requests:
126 status.Requests.Total = value
127 case connReading:
128 status.Connections.Reading = value
129 case connWriting:
130 status.Connections.Writing = value
131 case connWaiting:
132 status.Connections.Waiting = value
133 case requestTime:
134 status.Requests.Time = &value
135 }
136 }
137
138 return &status, nil
139 }
140
141 func mustParseInt(value string) int64 {
142 v, err := strconv.ParseInt(value, 10, 64)
143 if err != nil {
144 panic(err)
145 }
146 return v
147 }