Skip to content

netstat: Use procfs parsers for netstat, snmp and snmp6 - #3796

Open
neoLsH wants to merge 1 commit into
prometheus:masterfrom
neoLsH:netstat-use-procfs-parsers
Open

netstat: Use procfs parsers for netstat, snmp and snmp6#3796
neoLsH wants to merge 1 commit into
prometheus:masterfrom
neoLsH:netstat-use-procfs-parsers

Conversation

@neoLsH

@neoLsH neoLsH commented Aug 26, 2026

Copy link
Copy Markdown

What

Replace the hand-written parsers for /proc/net/netstat, /proc/net/snmp and
/proc/net/snmp6 with the parsers from prometheus/procfs
(Proc.Netstat(), Proc.Snmp(), Proc.Snmp6()), as requested in #2336.

This is a follow-up to #2360, which was closed as stale. Instead of declaring
one prometheus.Desc per field (~3000 lines), the metrics are exposed by
iterating the procfs statistics structs via reflection, the same approach the
NFS collector (collector/nfs_linux.go) already uses.

Details

  • /proc/net/* is network-namespace local; the procfs parsers operate on
    /proc/<pid>/net/*, so the collector now reads /proc/self/net/*. On Linux
    /proc/net is an alias of /proc/self/net, so the collected data and
    namespace semantics are unchanged.
  • procfs models the fields as *float64 (Replace float64 with *float64 for proc_netstat, proc_snmp and proc_snmp6 procfs#464): fields that
    are not present on the system are nil and skipped, so — like before — only
    statistics the kernel actually provides are exported.
  • The --collector.netstat.fields flag and its default are unchanged, as are
    the metric names, types and help strings. proc.Snmp6() already tolerates a
    missing snmp6 file on systems with IPv6 disabled.
  • The exported metrics are byte-for-byte identical: the e2e golden output
    (collector/fixtures/e2e-output.txt) required no changes.

Test fixtures

procfs.FS.Self() resolves the proc/self symlink in the fixtures (pid 10),
so collector/fixtures/proc/10/net was added as a symlink to ../net,
mirroring the kernel layout where /proc/<pid>/net is a per-netns symlink.

Verification

  • go test ./collector/ (includes a new TestNetStats comparing the full
    exposition against the expected 41 metrics via testutil.CollectAndCompare)
  • ./end-to-end-test.sh (golden output unchanged)

Fixes #2336

Replace the hand-written parsers for /proc/net/netstat, /proc/net/snmp
and /proc/net/snmp6 with the parsers from prometheus/procfs
(Proc.Netstat, Proc.Snmp and Proc.Snmp6). The statistics are read via
/proc/self/net, which is equivalent to /proc/net (both are
network-namespace local views of the current process), so the collected
data is unchanged.

Metrics are exposed by iterating the procfs statistics structs, the
same approach the NFS collector uses: fields are *float64 and only the
ones present on the system are exported, matching the previous
behavior. The --collector.netstat.fields flag and the exported metric
names are unchanged; the e2e golden output is identical.

Signed-off-by: neoLsH <43921685+neoLsH@users.noreply.github.com>
Comment on lines +101 to +103
// emitStruct emits one metric per non-nil field of a procfs netstat/snmp
// statistics struct, using the struct's type name as the protocol name.
func (c *netStatCollector) emitStruct(ch chan<- prometheus.Metric, stats any) {

@SuperQ SuperQ Aug 26, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When we are converting these collectors we are migrating to explicitly defined metric package var descriptors rather than dynamically generated Descs.

This reduces runtime allocs as well as makes it easier for us to do AST-generated documentation.

@neoLsH

neoLsH commented Aug 26, 2026

Copy link
Copy Markdown
Author

Thanks — that makes sense on both counts (per-scrape allocs and the AST-based doc generation in #3585). I'll rework the exposure layer accordingly, and I found a precedent I'd like to confirm: zoneinfo_linux.go builds an explicit field-name → prometheus.NewDesc map once in the constructor and still reflects over the procfs struct at collection time. Following that shape here keeps the descriptors as source-visible literals allocated once, without hand-expanding the emit loop. Let me know if you'd rather have one package-level *prometheus.Desc var per metric instead.

Two things worth deciding before I write it, since they change the diff shape:

1. Scale / splitting. The procfs structs expose 301 fields (netstat: TcpExt 113 + IpExt 18; snmp: 79; snmp6: 91), of which the default --collector.netstat.fields regex admits 42. For reference the largest explicit-descriptor collector today is mountstats_linux.go at 54. Happy to do one PR, or split it as netstat / snmp / snmp6 — your call on what's reviewable.

2. A metric-contract change I should flag explicitly. Diffing the old dynamic parser's output (using this repo's own fixtures) against the procfs struct fields, 10 metrics disappear: TcpExt_PAWSPassive, TcpExt_TCPPrequeued, TcpExt_TCPPrequeueDropped, TcpExt_TCPDirectCopyFromBacklog, TcpExt_TCPDirectCopyFromPrequeue, TcpExt_TCPLoss, TcpExt_TCPFACKReorder, TcpExt_TCPForwardRetrans, TcpExt_TCPHPHitsToUser, TcpExt_TCPSchedulerFailed. None of them match the default field regex, so neither the default scrape nor the e2e golden file can detect the loss — the green CI here doesn't cover it. More generally, the collector moves from "expose whatever the kernel prints" to "expose what procfs models", so newly added kernel counters will need a procfs update first. My suggestion is to accept this as an intentional change with a CHANGELOG note (those counters are legacy TcpExt fields that procfs deliberately doesn't model), but I'd rather have your call than assume — the alternative is keeping a fallback path for fields procfs doesn't model.

Also: should the help strings stay as today's Statistic <Proto><Field>., keeping this change purely mechanical and easy to review, or would you prefer real per-metric help text? That would be 301 strings, so I'd suggest a follow-up PR for it — which would also be useful input for #3585.

@SuperQ

SuperQ commented Aug 27, 2026

Copy link
Copy Markdown
Member

For the missing metrics if we're missing fields in procfs, we should update the parser there.

I wonder if it would help to do some refactoring of the procfs code to add some helper functions.

For example, the TcpExt struct fields are all *float64.

We could make this a custom type like

type StatValue struct {
  Value float64
  Name string
}

This would allow us to embed the original filename string so that we can more easily use it with the regexp matcher.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Replace netstat parsers with procfs netstat parsers

2 participants