github.com/netdata/go.d.plugin@v0.58.1/modules/phpfpm/decode.go (about)

     1  // SPDX-License-Identifier: GPL-3.0-or-later
     2  
     3  package phpfpm
     4  
     5  import (
     6  	"bufio"
     7  	"encoding/json"
     8  	"errors"
     9  	"io"
    10  	"strconv"
    11  	"strings"
    12  )
    13  
    14  type decoder func(r io.Reader, s *status) error
    15  
    16  func decodeJSON(r io.Reader, s *status) error {
    17  	return json.NewDecoder(r).Decode(s)
    18  }
    19  
    20  func decodeText(r io.Reader, s *status) error {
    21  	parts := readParts(r)
    22  	if len(parts) == 0 {
    23  		return errors.New("invalid text format")
    24  	}
    25  
    26  	part, parts := parts[0], parts[1:]
    27  	if err := readStatus(part, s); err != nil {
    28  		return err
    29  	}
    30  
    31  	return readProcesses(parts, s)
    32  }
    33  
    34  func readParts(r io.Reader) [][]string {
    35  	sc := bufio.NewScanner(r)
    36  
    37  	var parts [][]string
    38  	var lines []string
    39  	for sc.Scan() {
    40  		line := strings.Trim(sc.Text(), "\r\n ")
    41  		// Split parts by star border
    42  		if strings.HasPrefix(line, "***") {
    43  			parts = append(parts, lines)
    44  			lines = []string{}
    45  			continue
    46  		}
    47  		// Skip empty lines
    48  		if line == "" {
    49  			continue
    50  		}
    51  		lines = append(lines, line)
    52  	}
    53  
    54  	if len(lines) > 0 {
    55  		parts = append(parts, lines)
    56  	}
    57  	return parts
    58  }
    59  
    60  func readStatus(data []string, s *status) error {
    61  	for _, line := range data {
    62  		key, val, err := parseLine(line)
    63  		if err != nil {
    64  			return err
    65  		}
    66  
    67  		switch key {
    68  		case "active processes":
    69  			s.Active = parseInt(val)
    70  		case "max active processes":
    71  			s.MaxActive = parseInt(val)
    72  		case "idle processes":
    73  			s.Idle = parseInt(val)
    74  		case "accepted conn":
    75  			s.Requests = parseInt(val)
    76  		case "max children reached":
    77  			s.Reached = parseInt(val)
    78  		case "slow requests":
    79  			s.Slow = parseInt(val)
    80  		}
    81  	}
    82  	return nil
    83  }
    84  
    85  func readProcesses(procs [][]string, s *status) error {
    86  	for _, part := range procs {
    87  		var proc proc
    88  		for _, line := range part {
    89  			key, val, err := parseLine(line)
    90  			if err != nil {
    91  				return err
    92  			}
    93  
    94  			switch key {
    95  			case "state":
    96  				proc.State = val
    97  			case "request duration":
    98  				proc.Duration = requestDuration(parseInt(val))
    99  			case "last request cpu":
   100  				proc.CPU = parseFloat(val)
   101  			case "last request memory":
   102  				proc.Memory = parseInt(val)
   103  			}
   104  		}
   105  		s.Processes = append(s.Processes, proc)
   106  	}
   107  	return nil
   108  }
   109  
   110  func parseLine(s string) (string, string, error) {
   111  	kv := strings.SplitN(s, ":", 2)
   112  	if len(kv) != 2 {
   113  		return "", "", errors.New("invalid text format line")
   114  	}
   115  	return strings.TrimSpace(kv[0]), strings.TrimSpace(kv[1]), nil
   116  }
   117  
   118  func parseInt(s string) int64 {
   119  	val, err := strconv.ParseInt(strings.TrimSpace(s), 10, 64)
   120  	if err != nil {
   121  		return 0
   122  	}
   123  	return val
   124  }
   125  
   126  func parseFloat(s string) float64 {
   127  	val, err := strconv.ParseFloat(strings.TrimSpace(s), 64)
   128  	if err != nil {
   129  		return 0
   130  	}
   131  	return val
   132  }