firehol/netdata

View on GitHub
src/go/plugin/go.d/modules/phpfpm/decode.go

Summary

Maintainability
A
0 mins
Test Coverage
// SPDX-License-Identifier: GPL-3.0-or-later

package phpfpm

import (
    "bufio"
    "encoding/json"
    "errors"
    "io"
    "strconv"
    "strings"
)

type decoder func(r io.Reader, s *status) error

func decodeJSON(r io.Reader, s *status) error {
    return json.NewDecoder(r).Decode(s)
}

func decodeText(r io.Reader, s *status) error {
    parts := readParts(r)
    if len(parts) == 0 {
        return errors.New("invalid text format")
    }

    part, parts := parts[0], parts[1:]
    if err := readStatus(part, s); err != nil {
        return err
    }

    return readProcesses(parts, s)
}

func readParts(r io.Reader) [][]string {
    sc := bufio.NewScanner(r)

    var parts [][]string
    var lines []string
    for sc.Scan() {
        line := strings.Trim(sc.Text(), "\r\n ")
        // Split parts by star border
        if strings.HasPrefix(line, "***") {
            parts = append(parts, lines)
            lines = []string{}
            continue
        }
        // Skip empty lines
        if line == "" {
            continue
        }
        lines = append(lines, line)
    }

    if len(lines) > 0 {
        parts = append(parts, lines)
    }
    return parts
}

func readStatus(data []string, s *status) error {
    for _, line := range data {
        key, val, err := parseLine(line)
        if err != nil {
            return err
        }

        switch key {
        case "active processes":
            s.Active = parseInt(val)
        case "max active processes":
            s.MaxActive = parseInt(val)
        case "idle processes":
            s.Idle = parseInt(val)
        case "accepted conn":
            s.Requests = parseInt(val)
        case "max children reached":
            s.Reached = parseInt(val)
        case "slow requests":
            s.Slow = parseInt(val)
        }
    }
    return nil
}

func readProcesses(procs [][]string, s *status) error {
    for _, part := range procs {
        var proc proc
        for _, line := range part {
            key, val, err := parseLine(line)
            if err != nil {
                return err
            }

            switch key {
            case "state":
                proc.State = val
            case "request duration":
                proc.Duration = requestDuration(parseInt(val))
            case "last request cpu":
                proc.CPU = parseFloat(val)
            case "last request memory":
                proc.Memory = parseInt(val)
            }
        }
        s.Processes = append(s.Processes, proc)
    }
    return nil
}

func parseLine(s string) (string, string, error) {
    kv := strings.SplitN(s, ":", 2)
    if len(kv) != 2 {
        return "", "", errors.New("invalid text format line")
    }
    return strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1]), nil
}

func parseInt(s string) int64 {
    val, err := strconv.ParseInt(strings.TrimSpace(s), 10, 64)
    if err != nil {
        return 0
    }
    return val
}

func parseFloat(s string) float64 {
    val, err := strconv.ParseFloat(strings.TrimSpace(s), 64)
    if err != nil {
        return 0
    }
    return val
}