huatuo/core/metrics/qdisc.go

133 lines
3.8 KiB
Go

// Copyright 2025 The HuaTuo 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.
package collector
// ref: https://github.com/prometheus/node_exporter/tree/master/collector
// - qdisc_linux.go
import (
"huatuo-bamai/internal/conf"
"huatuo-bamai/pkg/metric"
"huatuo-bamai/pkg/tracing"
"github.com/ema/qdisc"
)
type qdiscStats struct {
ifaceName string
kind string
bytes uint64
packets uint32
drops uint32
requeues uint32
overlimits uint32
qlen uint32
backlog uint32
}
const tcHMajMask = 0xFFFF0000
type qdiscCollector struct{}
func init() {
tracing.RegisterEventTracing("qdisc", newQdiscCollector)
}
func newQdiscCollector() (*tracing.EventTracingAttr, error) {
return &tracing.EventTracingAttr{
TracingData: &qdiscCollector{},
Flag: tracing.FlagMetric,
}, nil
}
// sum of same level(parent major) for a device, example:
// <device0> (1+2, 3)
// 1: qidsc <kind> handle0 parent0
// 2: qidsc <kind> handle1 parent0
// 3: qidsc <kind> handle2 parent1
//
// <device1> (1, 2+3)
// 1: qidsc <kind> handle0 parent0
// 2: qidsc <kind> handle1 parent1
// 3: qidsc <kind> handle2 parent1
func (c *qdiscCollector) Update() ([]*metric.Data, error) {
filter := newFieldFilter(conf.Get().MetricCollector.Qdisc.IgnoredDevices,
conf.Get().MetricCollector.Qdisc.AcceptDevices)
allQdisc, err := qdisc.Get()
if err != nil {
return nil, err
}
allQdiscMap := make(map[string]map[uint32]*qdiscStats)
for _, q := range allQdisc {
if filter.ignored(q.IfaceName) || q.Kind == "noqueue" {
continue
}
parentMaj := (q.Parent & tcHMajMask) >> 16
if _, ok := allQdiscMap[q.IfaceName]; !ok {
allQdiscMap[q.IfaceName] = make(map[uint32]*qdiscStats)
}
netQdisc, ok := allQdiscMap[q.IfaceName][parentMaj]
if !ok {
allQdiscMap[q.IfaceName][parentMaj] = &qdiscStats{
ifaceName: q.IfaceName,
kind: q.Kind,
bytes: q.Bytes,
packets: q.Packets,
drops: q.Drops,
requeues: q.Requeues,
overlimits: q.Overlimits,
qlen: q.Qlen,
backlog: q.Backlog,
}
} else {
netQdisc.bytes += q.Bytes
netQdisc.packets += q.Packets
netQdisc.drops += q.Drops
netQdisc.requeues += q.Requeues
netQdisc.overlimits += q.Overlimits
netQdisc.qlen += q.Qlen
netQdisc.backlog += q.Backlog
}
}
var metrics []*metric.Data
for _, netdevQdisc := range allQdiscMap {
for _, oneQdisc := range netdevQdisc {
tags := map[string]string{"device": oneQdisc.ifaceName, "kind": oneQdisc.kind}
metrics = append(metrics,
metric.NewGaugeData("bytes_total", float64(oneQdisc.bytes),
"Number of bytes sent.", tags),
metric.NewGaugeData("packets_total", float64(oneQdisc.packets),
"Number of packets sent.", tags),
metric.NewGaugeData("drops_total", float64(oneQdisc.drops),
"Number of packet drops.", tags),
metric.NewGaugeData("requeues_total", float64(oneQdisc.requeues),
"Number of packets dequeued, not transmitted, and requeued.", tags),
metric.NewGaugeData("overlimits_total", float64(oneQdisc.overlimits),
"Number of packet overlimits.", tags),
metric.NewGaugeData("current_queue_length", float64(oneQdisc.qlen),
"Number of packets currently in queue to be sent.", tags),
metric.NewGaugeData("backlog", float64(oneQdisc.backlog),
"Number of bytes currently in queue to be sent.", tags),
)
}
}
return metrics, nil
}