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 }