src/go/plugin/go.d/modules/varnish/collect.go
// SPDX-License-Identifier: GPL-3.0-or-later
package varnish
import (
"bufio"
"bytes"
"strconv"
"strings"
)
func (v *Varnish) collect() (map[string]int64, error) {
bs, err := v.exec.statistics()
if err != nil {
return nil, err
}
mx := make(map[string]int64)
if err := v.collectStatistics(mx, bs); err != nil {
return nil, err
}
return mx, nil
}
func (v *Varnish) collectStatistics(mx map[string]int64, bs []byte) error {
seenBackends, seenStorages := make(map[string]bool), make(map[string]bool)
sc := bufio.NewScanner(bytes.NewReader(bs))
for sc.Scan() {
line := strings.TrimSpace(sc.Text())
if line == "" {
continue
}
parts := strings.Fields(line)
if len(parts) < 4 {
v.Debugf("invalid line format: '%s'. Expected at least 4 fields, skipping line.", line)
continue
}
fullMetric := parts[0]
valueStr := parts[1]
category, metric, ok := strings.Cut(fullMetric, ".")
if !ok {
v.Debugf("invalid metric format: '%s'. Expected 'category.metric', skipping metric.", fullMetric)
continue
}
value, err := strconv.ParseInt(valueStr, 10, 64)
if err != nil {
v.Debugf("failed to parse metric '%s' value '%s': %v, skipping metric", fullMetric, valueStr, err)
continue
}
switch category {
case "MGT":
if mgtMetrics[metric] {
mx[fullMetric] = value
}
case "MAIN":
if mainMetrics[metric] {
mx[fullMetric] = value
}
case "SMA", "SMF", "MSE":
storage, sMetric, ok := strings.Cut(metric, ".")
if !ok {
v.Debugf("invalid metric format: '%s'. Expected 'type.storage.metric', skipping metric.", fullMetric)
continue
}
fullStorage := category + "." + storage
if storageMetrics[sMetric] {
seenStorages[fullStorage] = true
mx[fullMetric] = value
}
case "VBE":
// Varnish 4.0.x is not supported (values are 'VBE.default(127.0.0.1,,81).happy')
parts := strings.Split(metric, ".")
if len(parts) != 3 {
v.Debugf("invalid metric format: '%s'. Expected 'VBE.vcl.backend.metric', skipping metric.", fullMetric)
continue
}
vcl, backend, bMetric := parts[0], parts[1], parts[2]
if backendMetrics[bMetric] {
seenBackends[vcl+"."+backend] = true
mx[fullMetric] = value
}
}
}
if len(mx) == 0 {
return nil
}
for name := range seenStorages {
if !v.seenStorages[name] {
v.seenStorages[name] = true
v.addStorageCharts(name)
}
}
for name := range v.seenStorages {
if !seenStorages[name] {
delete(v.seenStorages, name)
v.removeBackendCharts(name)
}
}
for fullName := range seenBackends {
if !v.seenBackends[fullName] {
v.seenBackends[fullName] = true
v.addBackendCharts(fullName)
}
}
for fullName := range v.seenBackends {
if !seenBackends[fullName] {
delete(v.seenBackends, fullName)
v.removeBackendCharts(fullName)
}
}
return nil
}
var mgtMetrics = map[string]bool{
"uptime": true,
"child_start": true,
"child_exit": true,
"child_stop": true,
"child_died": true,
"child_dump": true,
"child_panic": true,
}
var mainMetrics = map[string]bool{
"sess_conn": true,
"sess_dropped": true,
"client_req": true,
"cache_hit": true,
"cache_hitpass": true,
"cache_miss": true,
"cache_hitmiss": true,
"n_expired": true,
"n_lru_nuked": true,
"n_lru_moved": true,
"n_lru_limited": true,
"threads": true,
"threads_limited": true,
"threads_created": true,
"threads_destroyed": true,
"threads_failed": true,
"thread_queue_len": true,
"backend_conn": true,
"backend_unhealthy": true,
"backend_busy": true,
"backend_fail": true,
"backend_reuse": true,
"backend_recycle": true,
"backend_retry": true,
"backend_req": true,
"esi_errors": true,
"esi_warnings": true,
"uptime": true,
}
var storageMetrics = map[string]bool{
"g_space": true,
"g_bytes": true,
"g_alloc": true,
}
var backendMetrics = map[string]bool{
"bereq_hdrbytes": true,
"bereq_bodybytes": true,
"beresp_hdrbytes": true,
"beresp_bodybytes": true,
}