prometheusmetricshost-metricsmachine-metricsnode-metricsprocfsprometheus-exportersystem-informationsystem-metrics
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
184 lines
4.1 KiB
184 lines
4.1 KiB
// Copyright 2015 The Prometheus Authors |
|
// Licensed under the Apache License, Version 2.0 (the "License"); |
|
// you may not use this file except in compliance with the License. |
|
// You may obtain a copy of the License at |
|
// |
|
// http://www.apache.org/licenses/LICENSE-2.0 |
|
// |
|
// Unless required by applicable law or agreed to in writing, software |
|
// distributed under the License is distributed on an "AS IS" BASIS, |
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
|
// See the License for the specific language governing permissions and |
|
// limitations under the License. |
|
|
|
//go:build !nosockstat |
|
// +build !nosockstat |
|
|
|
package collector |
|
|
|
import ( |
|
"errors" |
|
"fmt" |
|
"log/slog" |
|
"os" |
|
|
|
"github.com/prometheus/client_golang/prometheus" |
|
"github.com/prometheus/procfs" |
|
) |
|
|
|
const ( |
|
sockStatSubsystem = "sockstat" |
|
) |
|
|
|
// Used for calculating the total memory bytes on TCP and UDP. |
|
var pageSize = os.Getpagesize() |
|
|
|
type sockStatCollector struct { |
|
logger *slog.Logger |
|
} |
|
|
|
func init() { |
|
registerCollector(sockStatSubsystem, defaultEnabled, NewSockStatCollector) |
|
} |
|
|
|
// NewSockStatCollector returns a new Collector exposing socket stats. |
|
func NewSockStatCollector(logger *slog.Logger) (Collector, error) { |
|
return &sockStatCollector{logger}, nil |
|
} |
|
|
|
func (c *sockStatCollector) Update(ch chan<- prometheus.Metric) error { |
|
fs, err := procfs.NewFS(*procPath) |
|
if err != nil { |
|
return fmt.Errorf("failed to open procfs: %w", err) |
|
} |
|
|
|
// If IPv4 and/or IPv6 are disabled on this kernel, handle it gracefully. |
|
stat4, err := fs.NetSockstat() |
|
switch { |
|
case err == nil: |
|
case errors.Is(err, os.ErrNotExist): |
|
c.logger.Debug("IPv4 sockstat statistics not found, skipping") |
|
default: |
|
return fmt.Errorf("failed to get IPv4 sockstat data: %w", err) |
|
} |
|
|
|
stat6, err := fs.NetSockstat6() |
|
switch { |
|
case err == nil: |
|
case errors.Is(err, os.ErrNotExist): |
|
c.logger.Debug("IPv6 sockstat statistics not found, skipping") |
|
default: |
|
return fmt.Errorf("failed to get IPv6 sockstat data: %w", err) |
|
} |
|
|
|
stats := []struct { |
|
isIPv6 bool |
|
stat *procfs.NetSockstat |
|
}{ |
|
{ |
|
stat: stat4, |
|
}, |
|
{ |
|
isIPv6: true, |
|
stat: stat6, |
|
}, |
|
} |
|
|
|
for _, s := range stats { |
|
c.update(ch, s.isIPv6, s.stat) |
|
} |
|
|
|
return nil |
|
} |
|
|
|
func (c *sockStatCollector) update(ch chan<- prometheus.Metric, isIPv6 bool, s *procfs.NetSockstat) { |
|
if s == nil { |
|
// IPv6 disabled or similar; nothing to do. |
|
return |
|
} |
|
|
|
// If sockstat contains the number of used sockets, export it. |
|
if !isIPv6 && s.Used != nil { |
|
// TODO: this must be updated if sockstat6 ever exports this data. |
|
ch <- prometheus.MustNewConstMetric( |
|
prometheus.NewDesc( |
|
prometheus.BuildFQName(namespace, sockStatSubsystem, "sockets_used"), |
|
"Number of IPv4 sockets in use.", |
|
nil, |
|
nil, |
|
), |
|
prometheus.GaugeValue, |
|
float64(*s.Used), |
|
) |
|
} |
|
|
|
// A name and optional value for a sockstat metric. |
|
type ssPair struct { |
|
name string |
|
v *int |
|
} |
|
|
|
// Previously these metric names were generated directly from the file output. |
|
// In order to keep the same level of compatibility, we must map the fields |
|
// to their correct names. |
|
for _, p := range s.Protocols { |
|
pairs := []ssPair{ |
|
{ |
|
name: "inuse", |
|
v: &p.InUse, |
|
}, |
|
{ |
|
name: "orphan", |
|
v: p.Orphan, |
|
}, |
|
{ |
|
name: "tw", |
|
v: p.TW, |
|
}, |
|
{ |
|
name: "alloc", |
|
v: p.Alloc, |
|
}, |
|
{ |
|
name: "mem", |
|
v: p.Mem, |
|
}, |
|
{ |
|
name: "memory", |
|
v: p.Memory, |
|
}, |
|
} |
|
|
|
// Also export mem_bytes values for sockets which have a mem value |
|
// stored in pages. |
|
if p.Mem != nil { |
|
v := *p.Mem * pageSize |
|
pairs = append(pairs, ssPair{ |
|
name: "mem_bytes", |
|
v: &v, |
|
}) |
|
} |
|
|
|
for _, pair := range pairs { |
|
if pair.v == nil { |
|
// This value is not set for this protocol; nothing to do. |
|
continue |
|
} |
|
|
|
ch <- prometheus.MustNewConstMetric( |
|
prometheus.NewDesc( |
|
prometheus.BuildFQName( |
|
namespace, |
|
sockStatSubsystem, |
|
fmt.Sprintf("%s_%s", p.Protocol, pair.name), |
|
), |
|
fmt.Sprintf("Number of %s sockets in state %s.", p.Protocol, pair.name), |
|
nil, |
|
nil, |
|
), |
|
prometheus.GaugeValue, |
|
float64(*pair.v), |
|
) |
|
} |
|
} |
|
}
|
|
|