io-metrics 0.1.0 → 0.2.1
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 +36 -26
- data/lib/io/metrics/listener/linux.rb +38 -21
- data/lib/io/metrics/listener.rb +14 -6
- data/lib/io/metrics/version.rb +1 -1
- data/readme.md +30 -0
- data/releases.md +11 -0
- data.tar.gz.sig +0 -0
- metadata +4 -3
- 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: 50d4b28faec3b5ae9389c3437d1e5687c1e54229d0abfd79305699e3f43e798b
|
|
4
|
+
data.tar.gz: 33db46c67476acedd0ee7695d437679ffe8e3602f60de2d15363e9b59423aab6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4131cbf7a3d8d15256972f057c24608ac1f9a72e958a6361ef24e36ceb97f669767e80dfc309d9c39df6d8aa530d10f82d93d9e53a61f7e6d2b1a47e16568434
|
|
7
|
+
data.tar.gz: b3c5ae946df3a0f7d1fe59d787f91aa0faf95c2f8b8353a2b132576325f033c3e7819613923b5ba79fe87e153424591fa66f3f1cfbc6030592bfa2d44e39619d
|
checksums.yaml.gz.sig
CHANGED
|
Binary file
|
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
# Released under the MIT License.
|
|
4
4
|
# Copyright, 2026, by Samuel Williams.
|
|
5
5
|
|
|
6
|
+
require "socket"
|
|
7
|
+
|
|
6
8
|
class IO
|
|
7
9
|
module Metrics
|
|
8
10
|
# Darwin (macOS) implementation of listener statistics using netstat -L.
|
|
@@ -14,31 +16,43 @@ class IO
|
|
|
14
16
|
File.executable?(NETSTAT)
|
|
15
17
|
end
|
|
16
18
|
|
|
17
|
-
# Parse an address from netstat format to
|
|
19
|
+
# Parse an address from netstat format to Addrinfo (TCP, numeric port).
|
|
18
20
|
# @parameter address [String] Address string from netstat, e.g. "127.0.0.1.50876" or "*.63703".
|
|
19
|
-
# @returns [
|
|
21
|
+
# @returns [Addrinfo | Nil] Addrinfo for the listener, or nil if the line cannot be parsed.
|
|
20
22
|
def self.parse_address(address)
|
|
21
23
|
# Handle wildcard addresses: *.port -> 0.0.0.0:port
|
|
22
24
|
if address.start_with?("*.")
|
|
23
|
-
port = address[2..-1]
|
|
24
|
-
return "0.0.0.0
|
|
25
|
+
port = address[2..-1].to_i
|
|
26
|
+
return Addrinfo.tcp("0.0.0.0", port)
|
|
25
27
|
end
|
|
26
28
|
|
|
27
29
|
# Handle IPv4 addresses: ip.port -> ip:port
|
|
28
30
|
if address =~ /^([0-9.]+)\.(\d+)$/
|
|
29
31
|
ip = $1
|
|
30
|
-
port = $2
|
|
31
|
-
return
|
|
32
|
+
port = $2.to_i
|
|
33
|
+
return Addrinfo.tcp(ip, port)
|
|
32
34
|
end
|
|
33
35
|
|
|
34
|
-
# Handle IPv6
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
# Handle IPv6 or other formats: best-effort via Addrinfo.parse
|
|
37
|
+
begin
|
|
38
|
+
Addrinfo.parse(address)
|
|
39
|
+
rescue ArgumentError, SocketError
|
|
40
|
+
nil
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Build a stable string key for TCP listener filter matching (same style as Linux / user filters).
|
|
45
|
+
def self.tcp_listener_key(addrinfo)
|
|
46
|
+
if addrinfo.ipv6?
|
|
47
|
+
"[#{addrinfo.ip_address}]:#{addrinfo.ip_port}"
|
|
48
|
+
else
|
|
49
|
+
"#{addrinfo.ip_address}:#{addrinfo.ip_port}"
|
|
50
|
+
end
|
|
37
51
|
end
|
|
38
52
|
|
|
39
53
|
# Parse netstat -L output and extract listener statistics.
|
|
40
54
|
# @parameter addresses [Array(String) | Nil] Optional filter for specific addresses.
|
|
41
|
-
# @returns [
|
|
55
|
+
# @returns [Array(Listener)] One entry per listening socket reported by netstat.
|
|
42
56
|
def self.capture_tcp(addresses = nil)
|
|
43
57
|
listeners = {}
|
|
44
58
|
address_filter = addresses ? addresses.map{|address| address.downcase}.to_set : nil
|
|
@@ -62,36 +76,32 @@ class IO
|
|
|
62
76
|
# incomplete_queue_length = $2.to_i # incomplete connections (SYN_RECV)
|
|
63
77
|
# maximum_queue_length = $3.to_i # maximum queue size
|
|
64
78
|
|
|
65
|
-
|
|
66
|
-
|
|
79
|
+
addrinfo = parse_address(local_address_raw)
|
|
80
|
+
next unless addrinfo
|
|
67
81
|
|
|
82
|
+
key = tcp_listener_key(addrinfo)
|
|
68
83
|
# Apply filter if specified
|
|
69
|
-
next if address_filter && !address_filter.include?(
|
|
84
|
+
next if address_filter && !address_filter.include?(key.downcase)
|
|
70
85
|
|
|
71
|
-
listeners[
|
|
72
|
-
listeners[
|
|
86
|
+
listeners[key] ||= Listener.new(addrinfo, 0, 0)
|
|
87
|
+
listeners[key].queue_size = queue_length
|
|
73
88
|
# active_connections set to 0 (can't reliably count per listener)
|
|
74
|
-
listeners[
|
|
89
|
+
listeners[key].active_connections = 0
|
|
75
90
|
end
|
|
76
91
|
end
|
|
77
92
|
end
|
|
78
93
|
|
|
79
|
-
return listeners
|
|
94
|
+
return listeners.values
|
|
80
95
|
rescue Errno::ENOENT, Errno::EACCES
|
|
81
|
-
return
|
|
96
|
+
return []
|
|
82
97
|
end
|
|
83
98
|
|
|
84
99
|
# Capture listener listeners for TCP sockets.
|
|
85
100
|
# @parameter addresses [Array(String) | Nil] TCP address(es) to capture, e.g. ["0.0.0.0:80"]. If nil, captures all.
|
|
86
101
|
# @parameter paths [Array(String) | Nil] Unix socket path(s) to capture (not supported on Darwin).
|
|
87
|
-
# @returns [
|
|
102
|
+
# @returns [Array(Listener)] TCP listeners from netstat.
|
|
88
103
|
def self.capture(addresses: nil, paths: nil)
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
# Capture TCP listeners (Unix sockets not supported on Darwin via netstat)
|
|
92
|
-
listeners.merge!(capture_tcp(addresses))
|
|
93
|
-
|
|
94
|
-
return listeners
|
|
104
|
+
capture_tcp(addresses)
|
|
95
105
|
end
|
|
96
106
|
end
|
|
97
107
|
end
|
|
@@ -109,7 +119,7 @@ if IO::Metrics::Listener::Darwin.supported?
|
|
|
109
119
|
# Capture listener listeners for the given address(es).
|
|
110
120
|
# @parameter addresses [Array(String) | Nil] TCP address(es) to capture, e.g. ["0.0.0.0:80"]. If nil, captures all listening TCP sockets.
|
|
111
121
|
# @parameter paths [Array(String) | Nil] Unix socket path(s) to capture (not supported on Darwin).
|
|
112
|
-
# @returns [
|
|
122
|
+
# @returns [Array(Listener) | Nil] Captured listeners, or nil if not supported.
|
|
113
123
|
def capture(**options)
|
|
114
124
|
IO::Metrics::Listener::Darwin.capture(**options)
|
|
115
125
|
end
|
|
@@ -103,7 +103,7 @@ class IO
|
|
|
103
103
|
|
|
104
104
|
# Find the best matching listener for an ESTABLISHED connection.
|
|
105
105
|
# @parameter local_address [String] Local address in "ip:port" or "[ipv6]:port" format.
|
|
106
|
-
# @parameter listeners [Hash(String, Listener)]
|
|
106
|
+
# @parameter listeners [Hash(String, Listener)] Internal map from display address string to Listener.
|
|
107
107
|
# @returns [String | Nil] The address of the matching listener, or nil if no match.
|
|
108
108
|
def self.find_matching_listener(local_address, listeners)
|
|
109
109
|
# Try exact match first
|
|
@@ -149,8 +149,15 @@ class IO
|
|
|
149
149
|
# @parameter file [String] Path to /proc/net/tcp or /proc/net/tcp6.
|
|
150
150
|
# @parameter addresses [Array(String) | Nil] Optional filter for specific addresses.
|
|
151
151
|
# @parameter ipv6 [Boolean] Whether parsing IPv6 addresses.
|
|
152
|
-
# @returns [
|
|
152
|
+
# @returns [Array(Listener)] One entry per listening socket.
|
|
153
153
|
def self.capture_tcp_file(file, addresses = nil, ipv6: false)
|
|
154
|
+
gather_tcp_file(file, addresses, ipv6: ipv6).values
|
|
155
|
+
rescue Errno::ENOENT, Errno::EACCES
|
|
156
|
+
return []
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Internal: same as capture_tcp_file but returns a Hash keyed by display address for merging and connection matching.
|
|
160
|
+
def self.gather_tcp_file(file, addresses = nil, ipv6: false)
|
|
154
161
|
listeners = {}
|
|
155
162
|
address_filter = addresses ? addresses.map{|address| address.downcase}.to_set : nil
|
|
156
163
|
connections = []
|
|
@@ -174,7 +181,8 @@ class IO
|
|
|
174
181
|
if state == :listen
|
|
175
182
|
if ipv6
|
|
176
183
|
local_ip = parse_ipv6(local_ip_hex)
|
|
177
|
-
|
|
184
|
+
local_port = parse_port(local_port_hex)
|
|
185
|
+
local_address = "[#{local_ip}]:#{local_port}"
|
|
178
186
|
else
|
|
179
187
|
local_ip = parse_ipv4(local_ip_hex)
|
|
180
188
|
local_port = parse_port(local_port_hex)
|
|
@@ -182,11 +190,12 @@ class IO
|
|
|
182
190
|
end
|
|
183
191
|
|
|
184
192
|
# Apply filter if specified
|
|
185
|
-
next if address_filter && !address_filter.include?(local_address)
|
|
193
|
+
next if address_filter && !address_filter.include?(local_address.downcase)
|
|
186
194
|
|
|
187
|
-
listeners[local_address] ||= Listener.
|
|
188
|
-
|
|
189
|
-
|
|
195
|
+
listeners[local_address] ||= Listener.new(Addrinfo.tcp(local_ip, local_port), 0, 0)
|
|
196
|
+
# rx_queue shows number of connections waiting to be accepted.
|
|
197
|
+
# Accumulate across SO_REUSEPORT sockets sharing the same address.
|
|
198
|
+
listeners[local_address].queue_size += rx_queue_hex.to_i(16)
|
|
190
199
|
listeners[local_address].active_connections = 0
|
|
191
200
|
# Collect ESTABLISHED connections to count later
|
|
192
201
|
elsif state == :established
|
|
@@ -210,6 +219,14 @@ class IO
|
|
|
210
219
|
end
|
|
211
220
|
end
|
|
212
221
|
|
|
222
|
+
# /proc lists every ESTABLISHED child with the listener's local address, including
|
|
223
|
+
# sockets still in the accept queue. Those are already counted in queue_size on the
|
|
224
|
+
# LISTEN row (same meaning as Raindrops inet_diag queued vs inode != 0 for active).
|
|
225
|
+
listeners.each_value do |listener|
|
|
226
|
+
backlog = listener.queue_size
|
|
227
|
+
listener.active_connections = [listener.active_connections - backlog, 0].max
|
|
228
|
+
end
|
|
229
|
+
|
|
213
230
|
return listeners
|
|
214
231
|
rescue Errno::ENOENT, Errno::EACCES
|
|
215
232
|
return {}
|
|
@@ -218,7 +235,7 @@ class IO
|
|
|
218
235
|
# Parse /proc/net/unix and extract listener statistics for Unix domain sockets.
|
|
219
236
|
# @parameter paths [Array(String) | Nil] Optional filter for specific socket paths.
|
|
220
237
|
# @parameter file [String] Optional path to Unix socket file (defaults to "/proc/net/unix").
|
|
221
|
-
# @returns [
|
|
238
|
+
# @returns [Array(Listener)] One entry per socket path with any matching activity.
|
|
222
239
|
def self.capture_unix(paths = nil, file: "/proc/net/unix")
|
|
223
240
|
listeners = {}
|
|
224
241
|
path_filter = paths ? paths.to_set : nil
|
|
@@ -246,7 +263,7 @@ class IO
|
|
|
246
263
|
|
|
247
264
|
state = state_hex.to_i(16)
|
|
248
265
|
|
|
249
|
-
listeners[path] ||= Listener.
|
|
266
|
+
listeners[path] ||= Listener.new(Addrinfo.unix(path), 0, 0)
|
|
250
267
|
|
|
251
268
|
case state
|
|
252
269
|
when SS_CONNECTING # Queued connections
|
|
@@ -256,37 +273,37 @@ class IO
|
|
|
256
273
|
end
|
|
257
274
|
end
|
|
258
275
|
|
|
259
|
-
return listeners
|
|
276
|
+
return listeners.values
|
|
260
277
|
rescue Errno::ENOENT, Errno::EACCES
|
|
261
|
-
return
|
|
278
|
+
return []
|
|
262
279
|
end
|
|
263
280
|
|
|
264
281
|
# Parse /proc/net/tcp and /proc/net/tcp6 and extract listener statistics.
|
|
265
282
|
# @parameter addresses [Array(String) | Nil] Optional filter for specific addresses.
|
|
266
|
-
# @returns [
|
|
283
|
+
# @returns [Array(Listener)] TCP listeners from both stacks.
|
|
267
284
|
def self.capture_tcp(addresses = nil)
|
|
268
285
|
listeners = {}
|
|
269
286
|
|
|
270
287
|
# Capture IPv4 listeners and connections
|
|
271
288
|
if File.readable?("/proc/net/tcp")
|
|
272
|
-
listeners.merge!(
|
|
289
|
+
listeners.merge!(gather_tcp_file("/proc/net/tcp", addresses, ipv6: false))
|
|
273
290
|
end
|
|
274
291
|
|
|
275
292
|
# Capture IPv6 listeners and connections
|
|
276
293
|
if File.readable?("/proc/net/tcp6")
|
|
277
|
-
listeners.merge!(
|
|
294
|
+
listeners.merge!(gather_tcp_file("/proc/net/tcp6", addresses, ipv6: true))
|
|
278
295
|
end
|
|
279
296
|
|
|
280
|
-
return listeners
|
|
297
|
+
return listeners.values
|
|
281
298
|
end
|
|
282
299
|
|
|
283
300
|
# Capture listener listeners for TCP and/or Unix domain sockets.
|
|
284
301
|
# @parameter addresses [Array(String) | Nil] TCP address(es) to capture, e.g. ["0.0.0.0:80"]. If nil and paths is nil, captures all. If nil but paths specified, captures none.
|
|
285
302
|
# @parameter paths [Array(String) | Nil] Unix socket path(s) to capture. If nil and addresses is nil, captures all. If nil but addresses specified, captures none.
|
|
286
303
|
# @parameter unix_file [String] Optional path to Unix socket file (defaults to "/proc/net/unix").
|
|
287
|
-
# @returns [
|
|
304
|
+
# @returns [Array(Listener)] All matching listeners (TCP and/or Unix).
|
|
288
305
|
def self.capture(addresses: nil, paths: nil, unix_file: "/proc/net/unix")
|
|
289
|
-
|
|
306
|
+
result = []
|
|
290
307
|
|
|
291
308
|
# If addresses are specified but paths is nil, don't capture Unix sockets
|
|
292
309
|
# Only capture Unix sockets if paths is explicitly provided or addresses is nil
|
|
@@ -294,12 +311,12 @@ class IO
|
|
|
294
311
|
unix_paths = paths.nil? && !addresses.nil? ? :skip : paths
|
|
295
312
|
|
|
296
313
|
# Capture TCP listeners (only if not skipped)
|
|
297
|
-
|
|
314
|
+
result.concat(capture_tcp(tcp_addresses)) unless tcp_addresses == :skip
|
|
298
315
|
|
|
299
316
|
# Capture Unix domain socket listeners (only if not skipped)
|
|
300
|
-
|
|
317
|
+
result.concat(capture_unix(unix_paths, file: unix_file)) unless unix_paths == :skip
|
|
301
318
|
|
|
302
|
-
return
|
|
319
|
+
return result
|
|
303
320
|
end
|
|
304
321
|
end
|
|
305
322
|
end
|
|
@@ -317,7 +334,7 @@ if IO::Metrics::Listener::Linux.supported?
|
|
|
317
334
|
# Capture listener listeners for the given address(es).
|
|
318
335
|
# @parameter addresses [Array(String) | Nil] TCP address(es) to capture, e.g. ["0.0.0.0:80"]. If nil, captures all listening TCP sockets.
|
|
319
336
|
# @parameter paths [Array(String) | Nil] Unix socket path(s) to capture. If nil and addresses is nil, captures all. If nil but addresses specified, captures none.
|
|
320
|
-
# @returns [
|
|
337
|
+
# @returns [Array(Listener) | Nil] Captured listeners, or nil if not supported.
|
|
321
338
|
def capture(**options)
|
|
322
339
|
IO::Metrics::Listener::Linux.capture(**options)
|
|
323
340
|
end
|
data/lib/io/metrics/listener.rb
CHANGED
|
@@ -8,20 +8,28 @@ require "json"
|
|
|
8
8
|
class IO
|
|
9
9
|
module Metrics
|
|
10
10
|
# Represents a network listener socket with its queue statistics.
|
|
11
|
+
# @attribute address [Addrinfo | Nil] Listening endpoint from capture; nil only for {Listener.zero} placeholders.
|
|
11
12
|
# @attribute queue_size [Integer] Number of connections waiting to be accepted (queued).
|
|
12
13
|
# @attribute active_connections [Integer] Number of active connections (already accepted).
|
|
13
|
-
class Listener < Struct.new(:queue_size, :active_connections)
|
|
14
|
-
|
|
14
|
+
class Listener < Struct.new(:address, :queue_size, :active_connections)
|
|
15
|
+
# Serialize for JSON; address uses Addrinfo#inspect_sockaddr.
|
|
16
|
+
def as_json(*)
|
|
17
|
+
{
|
|
18
|
+
address: address&.inspect_sockaddr,
|
|
19
|
+
queue_size: queue_size,
|
|
20
|
+
active_connections: active_connections,
|
|
21
|
+
}
|
|
22
|
+
end
|
|
15
23
|
|
|
16
24
|
# Convert the object to a JSON string.
|
|
17
25
|
def to_json(*arguments)
|
|
18
26
|
as_json.to_json(*arguments)
|
|
19
27
|
end
|
|
20
28
|
|
|
21
|
-
# Create a zero-initialized Listener instance.
|
|
22
|
-
# @returns [Listener]
|
|
29
|
+
# Create a zero-initialized Listener instance (no endpoint; for tests or templates).
|
|
30
|
+
# @returns [Listener] Counters zero; {#address} is nil.
|
|
23
31
|
def self.zero
|
|
24
|
-
|
|
32
|
+
new(nil, 0, 0)
|
|
25
33
|
end
|
|
26
34
|
|
|
27
35
|
# Whether listener stats can be captured on this system.
|
|
@@ -32,7 +40,7 @@ class IO
|
|
|
32
40
|
# Capture listener stats for the given address(es).
|
|
33
41
|
# @parameter addresses [Array(String) | Nil] TCP address(es) to capture, e.g. ["0.0.0.0:80"]. If nil, captures all listening TCP sockets.
|
|
34
42
|
# @parameter paths [Array(String) | Nil] Unix socket path(s) to capture. If nil and addresses is nil, captures all. If nil but addresses specified, captures none.
|
|
35
|
-
# @returns [
|
|
43
|
+
# @returns [Array(Listener) | Nil] Captured listeners, or nil if not supported.
|
|
36
44
|
def self.capture(**options)
|
|
37
45
|
return nil
|
|
38
46
|
end
|
data/lib/io/metrics/version.rb
CHANGED
data/readme.md
CHANGED
|
@@ -10,6 +10,20 @@ Please see the [project documentation](https://socketry.github.io/io-metrics/) f
|
|
|
10
10
|
|
|
11
11
|
- [Getting Started](https://socketry.github.io/io-metrics/guides/getting-started/index) - This guide explains how to use `io-metrics` to capture listener queue statistics from the host operating system.
|
|
12
12
|
|
|
13
|
+
## Releases
|
|
14
|
+
|
|
15
|
+
Please see the [project releases](https://socketry.github.io/io-metrics/releases/index) for all releases.
|
|
16
|
+
|
|
17
|
+
### v0.2.1
|
|
18
|
+
|
|
19
|
+
- 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.
|
|
20
|
+
- **Linux** `Listener#active_connections` for TCP no longer counts sockets that are still in the kernel accept queue (those remain in `queue_size`). Counts now match the usual “past `accept()`” meaning and align with tools such as Raindrops’ `ListenStats#active`.
|
|
21
|
+
|
|
22
|
+
### v0.2.0
|
|
23
|
+
|
|
24
|
+
- **Breaking** `IO::Metrics::Listener.capture` returns an `Array` of `Listener` rows instead of a `Hash` keyed by address string.
|
|
25
|
+
- Each `Listener` has `address` (`Addrinfo` for TCP or Unix), `queue_size`, and `active_connections`. `Listener.zero` sets `address` to `nil`. JSON uses `Addrinfo#inspect_sockaddr` for `address`, or `null` when absent.
|
|
26
|
+
|
|
13
27
|
## Contributing
|
|
14
28
|
|
|
15
29
|
We welcome contributions to this project.
|
|
@@ -20,6 +34,22 @@ We welcome contributions to this project.
|
|
|
20
34
|
4. Push to the branch (`git push origin my-new-feature`).
|
|
21
35
|
5. Create new Pull Request.
|
|
22
36
|
|
|
37
|
+
### Running Tests
|
|
38
|
+
|
|
39
|
+
To run the test suite:
|
|
40
|
+
|
|
41
|
+
``` shell
|
|
42
|
+
bundle exec sus
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Making Releases
|
|
46
|
+
|
|
47
|
+
To make a new release:
|
|
48
|
+
|
|
49
|
+
``` shell
|
|
50
|
+
bundle exec bake gem:release:patch # or minor or major
|
|
51
|
+
```
|
|
52
|
+
|
|
23
53
|
### Developer Certificate of Origin
|
|
24
54
|
|
|
25
55
|
In order to protect users of this project, we require all contributors to comply with the [Developer Certificate of Origin](https://developercertificate.org/). This ensures that all contributions are properly licensed and attributed.
|
data/releases.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# Releases
|
|
2
|
+
|
|
3
|
+
## v0.2.1
|
|
4
|
+
|
|
5
|
+
- 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.
|
|
6
|
+
- **Linux** `Listener#active_connections` for TCP no longer counts sockets that are still in the kernel accept queue (those remain in `queue_size`). Counts now match the usual “past `accept()`” meaning and align with tools such as Raindrops’ `ListenStats#active`.
|
|
7
|
+
|
|
8
|
+
## v0.2.0
|
|
9
|
+
|
|
10
|
+
- **Breaking** `IO::Metrics::Listener.capture` returns an `Array` of `Listener` rows instead of a `Hash` keyed by address string.
|
|
11
|
+
- Each `Listener` has `address` (`Addrinfo` for TCP or Unix), `queue_size`, and `active_connections`. `Listener.zero` sets `address` to `nil`. JSON uses `Addrinfo#inspect_sockaddr` for `address`, or `null` when absent.
|
data.tar.gz.sig
CHANGED
|
Binary file
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: io-metrics
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1
|
|
4
|
+
version: 0.2.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Samuel Williams
|
|
@@ -77,6 +77,7 @@ files:
|
|
|
77
77
|
- lib/io/metrics/version.rb
|
|
78
78
|
- license.md
|
|
79
79
|
- readme.md
|
|
80
|
+
- releases.md
|
|
80
81
|
homepage: https://github.com/socketry/io-metrics
|
|
81
82
|
licenses:
|
|
82
83
|
- MIT
|
|
@@ -91,14 +92,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
91
92
|
requirements:
|
|
92
93
|
- - ">="
|
|
93
94
|
- !ruby/object:Gem::Version
|
|
94
|
-
version: '3.
|
|
95
|
+
version: '3.3'
|
|
95
96
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
96
97
|
requirements:
|
|
97
98
|
- - ">="
|
|
98
99
|
- !ruby/object:Gem::Version
|
|
99
100
|
version: '0'
|
|
100
101
|
requirements: []
|
|
101
|
-
rubygems_version: 4.0.
|
|
102
|
+
rubygems_version: 4.0.6
|
|
102
103
|
specification_version: 4
|
|
103
104
|
summary: Extract I/O metrics from the host system.
|
|
104
105
|
test_files: []
|
metadata.gz.sig
CHANGED
|
Binary file
|