io-metrics 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/io/metrics/listener/darwin.rb +6 -4
- data/lib/io/metrics/listener/linux.rb +32 -16
- data/lib/io/metrics/listener.rb +8 -6
- data/lib/io/metrics/version.rb +1 -1
- data/readme.md +5 -0
- data/releases.md +5 -0
- data.tar.gz.sig +0 -0
- metadata +1 -1
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f7f07281a060f8644b5f50c62d526bfca3ea15ecb8a63001192f617b0aa7690d
|
|
4
|
+
data.tar.gz: ae6e4568aeae519c4e69643cc60a9203e0549408db787148b035151462a12819
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 69bc0a438888c3f4a58790d53aa09919110e7c538c6cbdbf04165c2c6472043a47844b5b61a5d8db61df739289ea806c7f906422c734a0a3a0f3c727b7756624
|
|
7
|
+
data.tar.gz: a3a052adb78a2d63276282795ffc9c2d3d691f5afc7dbc2313a9fe528a1f4ee5d82157b050aef6514d52b6cf8025ff4c3344d59df41782653916bbc3491069c2
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
|
@@ -83,10 +83,12 @@ class IO
|
|
|
83
83
|
# Apply filter if specified
|
|
84
84
|
next if address_filter && !address_filter.include?(key.downcase)
|
|
85
85
|
|
|
86
|
-
listeners[key] ||= Listener.new(addrinfo, 0, 0)
|
|
87
|
-
listeners[key].
|
|
88
|
-
|
|
89
|
-
|
|
86
|
+
listeners[key] ||= Listener.new(addrinfo, 0, 0, 0)
|
|
87
|
+
listeners[key].queued_count = queue_length
|
|
88
|
+
|
|
89
|
+
# active_count and close_wait_count set to 0 (netstat -L doesn't expose connection states)
|
|
90
|
+
listeners[key].active_count = 0
|
|
91
|
+
listeners[key].close_wait_count = 0
|
|
90
92
|
end
|
|
91
93
|
end
|
|
92
94
|
end
|
|
@@ -161,8 +161,9 @@ class IO
|
|
|
161
161
|
listeners = {}
|
|
162
162
|
address_filter = addresses ? addresses.map{|address| address.downcase}.to_set : nil
|
|
163
163
|
connections = []
|
|
164
|
+
close_wait_connections = []
|
|
164
165
|
|
|
165
|
-
# Single pass: collect LISTEN sockets and
|
|
166
|
+
# Single pass: collect LISTEN sockets, ESTABLISHED, and CLOSE_WAIT connections
|
|
166
167
|
File.foreach(file) do |line|
|
|
167
168
|
next if line.start_with?("sl")
|
|
168
169
|
|
|
@@ -192,13 +193,14 @@ class IO
|
|
|
192
193
|
# Apply filter if specified
|
|
193
194
|
next if address_filter && !address_filter.include?(local_address.downcase)
|
|
194
195
|
|
|
195
|
-
listeners[local_address] ||= Listener.new(Addrinfo.tcp(local_ip, local_port), 0, 0)
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
listeners[local_address].
|
|
200
|
-
|
|
201
|
-
|
|
196
|
+
listeners[local_address] ||= Listener.new(Addrinfo.tcp(local_ip, local_port), 0, 0, 0)
|
|
197
|
+
# rx_queue shows number of connections waiting to be accepted.
|
|
198
|
+
# Accumulate across SO_REUSEPORT sockets sharing the same address.
|
|
199
|
+
listeners[local_address].queued_count += rx_queue_hex.to_i(16)
|
|
200
|
+
listeners[local_address].active_count = 0
|
|
201
|
+
listeners[local_address].close_wait_count = 0
|
|
202
|
+
# Collect ESTABLISHED and CLOSE_WAIT connections to count later
|
|
203
|
+
elsif state == :established || state == :close_wait
|
|
202
204
|
if ipv6
|
|
203
205
|
local_ip = parse_ipv6(local_ip_hex)
|
|
204
206
|
local_address = "[#{local_ip}]:#{parse_port(local_port_hex)}"
|
|
@@ -207,7 +209,11 @@ class IO
|
|
|
207
209
|
local_port = parse_port(local_port_hex)
|
|
208
210
|
local_address = "#{local_ip}:#{local_port}"
|
|
209
211
|
end
|
|
210
|
-
|
|
212
|
+
if state == :established
|
|
213
|
+
connections << local_address
|
|
214
|
+
else
|
|
215
|
+
close_wait_connections << local_address
|
|
216
|
+
end
|
|
211
217
|
end
|
|
212
218
|
end
|
|
213
219
|
end
|
|
@@ -215,16 +221,26 @@ class IO
|
|
|
215
221
|
# Count ESTABLISHED connections for each listener
|
|
216
222
|
connections.each do |local_address|
|
|
217
223
|
if listener_address = find_matching_listener(local_address, listeners)
|
|
218
|
-
listeners[listener_address].
|
|
224
|
+
listeners[listener_address].active_count += 1
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# Count CLOSE_WAIT connections for each listener.
|
|
229
|
+
# These are accepted connections where the peer has closed its end but the
|
|
230
|
+
# application has not yet closed its side (e.g. still in rack.response_finished
|
|
231
|
+
# callbacks, or processing a request whose upstream already disconnected).
|
|
232
|
+
close_wait_connections.each do |local_address|
|
|
233
|
+
if listener_address = find_matching_listener(local_address, listeners)
|
|
234
|
+
listeners[listener_address].close_wait_count += 1
|
|
219
235
|
end
|
|
220
236
|
end
|
|
221
237
|
|
|
222
238
|
# /proc lists every ESTABLISHED child with the listener's local address, including
|
|
223
|
-
# sockets still in the accept queue. Those are already counted in
|
|
239
|
+
# sockets still in the accept queue. Those are already counted in queued_count on the
|
|
224
240
|
# LISTEN row (same meaning as Raindrops inet_diag queued vs inode != 0 for active).
|
|
225
241
|
listeners.each_value do |listener|
|
|
226
|
-
backlog = listener.
|
|
227
|
-
listener.
|
|
242
|
+
backlog = listener.queued_count
|
|
243
|
+
listener.active_count = [listener.active_count - backlog, 0].max
|
|
228
244
|
end
|
|
229
245
|
|
|
230
246
|
return listeners
|
|
@@ -263,13 +279,13 @@ class IO
|
|
|
263
279
|
|
|
264
280
|
state = state_hex.to_i(16)
|
|
265
281
|
|
|
266
|
-
listeners[path] ||= Listener.new(Addrinfo.unix(path), 0, 0)
|
|
282
|
+
listeners[path] ||= Listener.new(Addrinfo.unix(path), 0, 0, 0)
|
|
267
283
|
|
|
268
284
|
case state
|
|
269
285
|
when SS_CONNECTING # Queued connections
|
|
270
|
-
listeners[path].
|
|
286
|
+
listeners[path].queued_count += 1
|
|
271
287
|
when SS_CONNECTED # Active connections
|
|
272
|
-
listeners[path].
|
|
288
|
+
listeners[path].active_count += 1
|
|
273
289
|
end
|
|
274
290
|
end
|
|
275
291
|
|
data/lib/io/metrics/listener.rb
CHANGED
|
@@ -9,15 +9,17 @@ class IO
|
|
|
9
9
|
module Metrics
|
|
10
10
|
# Represents a network listener socket with its queue statistics.
|
|
11
11
|
# @attribute address [Addrinfo | Nil] Listening endpoint from capture; nil only for {Listener.zero} placeholders.
|
|
12
|
-
# @attribute
|
|
13
|
-
# @attribute
|
|
14
|
-
|
|
12
|
+
# @attribute queued_count [Integer] Number of connections waiting to be accepted (currently in the accept queue).
|
|
13
|
+
# @attribute active_count [Integer] Number of accepted connections in ESTABLISHED state.
|
|
14
|
+
# @attribute close_wait_count [Integer] Number of accepted connections in CLOSE_WAIT state (peer has closed; application still processing).
|
|
15
|
+
class Listener < Struct.new(:address, :queued_count, :active_count, :close_wait_count)
|
|
15
16
|
# Serialize for JSON; address uses Addrinfo#inspect_sockaddr.
|
|
16
17
|
def as_json(*)
|
|
17
18
|
{
|
|
18
19
|
address: address&.inspect_sockaddr,
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
queued_count: queued_count,
|
|
21
|
+
active_count: active_count,
|
|
22
|
+
close_wait_count: close_wait_count,
|
|
21
23
|
}
|
|
22
24
|
end
|
|
23
25
|
|
|
@@ -29,7 +31,7 @@ class IO
|
|
|
29
31
|
# Create a zero-initialized Listener instance (no endpoint; for tests or templates).
|
|
30
32
|
# @returns [Listener] Counters zero; {#address} is nil.
|
|
31
33
|
def self.zero
|
|
32
|
-
new(nil, 0, 0)
|
|
34
|
+
new(nil, 0, 0, 0) # address, queued_count, active_count, close_wait_count
|
|
33
35
|
end
|
|
34
36
|
|
|
35
37
|
# Whether listener stats can be captured on this system.
|
data/lib/io/metrics/version.rb
CHANGED
data/readme.md
CHANGED
|
@@ -14,6 +14,11 @@ Please see the [project documentation](https://socketry.github.io/io-metrics/) f
|
|
|
14
14
|
|
|
15
15
|
Please see the [project releases](https://socketry.github.io/io-metrics/releases/index) for all releases.
|
|
16
16
|
|
|
17
|
+
### v0.3.0
|
|
18
|
+
|
|
19
|
+
- **Breaking** Rename `Listener` fields: `queue_size` → `queued_count`, `active_connections` → `active_count`.
|
|
20
|
+
- Introduce `Listener#close_wait_count`: number of accepted connections in `CLOSE_WAIT` state (peer has closed; application still processing).
|
|
21
|
+
|
|
17
22
|
### v0.2.1
|
|
18
23
|
|
|
19
24
|
- Fixed `queue_size` under-reporting when multiple `SO_REUSEPORT` sockets share the same address — queue depths are now accumulated across all sockets rather than overwritten by the last one.
|
data/releases.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Releases
|
|
2
2
|
|
|
3
|
+
## v0.3.0
|
|
4
|
+
|
|
5
|
+
- **Breaking** Rename `Listener` fields: `queue_size` → `queued_count`, `active_connections` → `active_count`.
|
|
6
|
+
- Introduce `Listener#close_wait_count`: number of accepted connections in `CLOSE_WAIT` state (peer has closed; application still processing).
|
|
7
|
+
|
|
3
8
|
## v0.2.1
|
|
4
9
|
|
|
5
10
|
- Fixed `queue_size` under-reporting when multiple `SO_REUSEPORT` sockets share the same address — queue depths are now accumulated across all sockets rather than overwritten by the last one.
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
metadata.gz.sig
CHANGED
|
Binary file
|