firehol/netdata

View on GitHub
src/go/plugin/go.d/modules/varnish/collect.go

Summary

Maintainability
B
7 hrs
Test Coverage
// 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,
}