// Copyright 2017 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 !noqdisc // +build !noqdisc package collector import ( "encoding/json" "fmt" "log/slog" "os" "path/filepath" "github.com/alecthomas/kingpin/v2" "github.com/ema/qdisc" "github.com/prometheus/client_golang/prometheus" ) type qdiscStatCollector struct { logger *slog.Logger deviceFilter deviceFilter bytes typedDesc packets typedDesc drops typedDesc requeues typedDesc overlimits typedDesc qlength typedDesc backlog typedDesc } var ( collectorQdisc = kingpin.Flag("collector.qdisc.fixtures", "test fixtures to use for qdisc collector end-to-end testing").Default("").String() collectorQdiscDeviceInclude = kingpin.Flag("collector.qdisc.device-include", "Regexp of qdisc devices to include (mutually exclusive to device-exclude).").String() oldCollectorQdiskDeviceInclude = kingpin.Flag("collector.qdisk.device-include", "DEPRECATED: Use collector.qdisc.device-include").Hidden().String() collectorQdiscDeviceExclude = kingpin.Flag("collector.qdisc.device-exclude", "Regexp of qdisc devices to exclude (mutually exclusive to device-include).").String() oldCollectorQdiskDeviceExclude = kingpin.Flag("collector.qdisk.device-exclude", "DEPRECATED: Use collector.qdisc.device-exclude").Hidden().String() ) func init() { registerCollector("qdisc", defaultDisabled, NewQdiscStatCollector) } // NewQdiscStatCollector returns a new Collector exposing queuing discipline statistics. func NewQdiscStatCollector(logger *slog.Logger) (Collector, error) { if *oldCollectorQdiskDeviceInclude != "" { if *collectorQdiscDeviceInclude == "" { logger.Warn("--collector.qdisk.device-include is DEPRECATED and will be removed in 2.0.0, use --collector.qdisc.device-include") *collectorQdiscDeviceInclude = *oldCollectorQdiskDeviceInclude } else { return nil, fmt.Errorf("--collector.qdisk.device-include and --collector.qdisc.device-include are mutually exclusive") } } if *oldCollectorQdiskDeviceExclude != "" { if *collectorQdiscDeviceExclude == "" { logger.Warn("--collector.qdisk.device-exclude is DEPRECATED and will be removed in 2.0.0, use --collector.qdisc.device-exclude") *collectorQdiscDeviceExclude = *oldCollectorQdiskDeviceExclude } else { return nil, fmt.Errorf("--collector.qdisk.device-exclude and --collector.qdisc.device-exclude are mutually exclusive") } } if *collectorQdiscDeviceExclude != "" && *collectorQdiscDeviceInclude != "" { return nil, fmt.Errorf("collector.qdisc.device-include and collector.qdisc.device-exclude are mutaly exclusive") } return &qdiscStatCollector{ bytes: typedDesc{prometheus.NewDesc( prometheus.BuildFQName(namespace, "qdisc", "bytes_total"), "Number of bytes sent.", []string{"device", "kind"}, nil, ), prometheus.CounterValue}, packets: typedDesc{prometheus.NewDesc( prometheus.BuildFQName(namespace, "qdisc", "packets_total"), "Number of packets sent.", []string{"device", "kind"}, nil, ), prometheus.CounterValue}, drops: typedDesc{prometheus.NewDesc( prometheus.BuildFQName(namespace, "qdisc", "drops_total"), "Number of packets dropped.", []string{"device", "kind"}, nil, ), prometheus.CounterValue}, requeues: typedDesc{prometheus.NewDesc( prometheus.BuildFQName(namespace, "qdisc", "requeues_total"), "Number of packets dequeued, not transmitted, and requeued.", []string{"device", "kind"}, nil, ), prometheus.CounterValue}, overlimits: typedDesc{prometheus.NewDesc( prometheus.BuildFQName(namespace, "qdisc", "overlimits_total"), "Number of overlimit packets.", []string{"device", "kind"}, nil, ), prometheus.CounterValue}, qlength: typedDesc{prometheus.NewDesc( prometheus.BuildFQName(namespace, "qdisc", "current_queue_length"), "Number of packets currently in queue to be sent.", []string{"device", "kind"}, nil, ), prometheus.GaugeValue}, backlog: typedDesc{prometheus.NewDesc( prometheus.BuildFQName(namespace, "qdisc", "backlog"), "Number of bytes currently in queue to be sent.", []string{"device", "kind"}, nil, ), prometheus.GaugeValue}, logger: logger, deviceFilter: newDeviceFilter(*collectorQdiscDeviceExclude, *collectorQdiscDeviceInclude), }, nil } func testQdiscGet(fixtures string) ([]qdisc.QdiscInfo, error) { var res []qdisc.QdiscInfo b, err := os.ReadFile(filepath.Join(fixtures, "results.json")) if err != nil { return res, err } err = json.Unmarshal(b, &res) return res, err } func (c *qdiscStatCollector) Update(ch chan<- prometheus.Metric) error { var msgs []qdisc.QdiscInfo var err error fixtures := *collectorQdisc if fixtures == "" { msgs, err = qdisc.Get() } else { msgs, err = testQdiscGet(fixtures) } if err != nil { return err } for _, msg := range msgs { // Only report root qdisc information. if msg.Parent != 0 { continue } if c.deviceFilter.ignored(msg.IfaceName) { continue } ch <- c.bytes.mustNewConstMetric(float64(msg.Bytes), msg.IfaceName, msg.Kind) ch <- c.packets.mustNewConstMetric(float64(msg.Packets), msg.IfaceName, msg.Kind) ch <- c.drops.mustNewConstMetric(float64(msg.Drops), msg.IfaceName, msg.Kind) ch <- c.requeues.mustNewConstMetric(float64(msg.Requeues), msg.IfaceName, msg.Kind) ch <- c.overlimits.mustNewConstMetric(float64(msg.Overlimits), msg.IfaceName, msg.Kind) ch <- c.qlength.mustNewConstMetric(float64(msg.Qlen), msg.IfaceName, msg.Kind) ch <- c.backlog.mustNewConstMetric(float64(msg.Backlog), msg.IfaceName, msg.Kind) } return nil }