github.com/netdata/go.d.plugin@v0.58.1/modules/pika/collect_info.go (about) 1 // SPDX-License-Identifier: GPL-3.0-or-later 2 3 package pika 4 5 import ( 6 "bufio" 7 "regexp" 8 "strconv" 9 "strings" 10 11 "github.com/netdata/go.d.plugin/agent/module" 12 ) 13 14 // https://github.com/Qihoo360/pika/blob/master/src/pika_admin.cc 15 // https://github.com/Qihoo360/pika/blob/a0dbdcf5897dd7800ba8a4d1eafce1595619ddc8/src/pika_admin.cc#L694-L710 16 17 const ( 18 infoSectionServer = "# Server" 19 infoSectionData = "# Data" 20 infoSectionClients = "# Clients" 21 infoSectionStats = "# Stats" 22 infoSectionCommandExecCount = "# Command_Exec_Count" 23 infoSectionCPU = "# CPU" 24 infoSectionReplMaster = "# Replication(MASTER)" 25 infoSectionReplSlave = "# Replication(SLAVE)" 26 infoSectionReplMasterSlave = "# Replication(Master && SLAVE)" 27 infoSectionKeyspace = "# Keyspace" 28 ) 29 30 var infoSections = map[string]struct{}{ 31 infoSectionServer: {}, 32 infoSectionData: {}, 33 infoSectionClients: {}, 34 infoSectionStats: {}, 35 infoSectionCommandExecCount: {}, 36 infoSectionCPU: {}, 37 infoSectionReplMaster: {}, 38 infoSectionReplSlave: {}, 39 infoSectionReplMasterSlave: {}, 40 infoSectionKeyspace: {}, 41 } 42 43 func isInfoSection(line string) bool { _, ok := infoSections[line]; return ok } 44 45 func (p *Pika) collectInfo(ms map[string]int64, info string) { 46 var curSection string 47 48 sc := bufio.NewScanner(strings.NewReader(info)) 49 for sc.Scan() { 50 line := strings.TrimSpace(sc.Text()) 51 if len(line) == 0 { 52 curSection = "" 53 continue 54 } 55 if strings.HasPrefix(line, "#") { 56 if isInfoSection(line) { 57 curSection = line 58 } 59 continue 60 } 61 62 field, value, ok := parseProperty(line) 63 if !ok { 64 continue 65 } 66 67 switch curSection { 68 case infoSectionCommandExecCount: 69 p.collectInfoCommandExecCountProperty(ms, field, value) 70 case infoSectionKeyspace: 71 p.collectInfoKeyspaceProperty(ms, field, value) 72 default: 73 collectNumericValue(ms, field, value) 74 } 75 } 76 } 77 78 var reKeyspaceValue = regexp.MustCompile(`^(.+)_keys=(\d+), expires=(\d+), invalid_keys=(\d+)`) 79 80 func (p *Pika) collectInfoKeyspaceProperty(ms map[string]int64, field, value string) { 81 match := reKeyspaceValue.FindStringSubmatch(value) 82 if match == nil { 83 return 84 } 85 86 dataType, keys, expires, invalid := strings.ToLower(match[1]), match[2], match[3], match[4] 87 collectNumericValue(ms, field+"_"+dataType+"_keys", keys) 88 collectNumericValue(ms, field+"_"+dataType+"_expires_keys", expires) 89 collectNumericValue(ms, field+"_"+dataType+"_invalid_keys", invalid) 90 91 if !p.collectedDbs[field] { 92 p.collectedDbs[field] = true 93 p.addDbToKeyspaceCharts(field) 94 } 95 } 96 97 func (p *Pika) collectInfoCommandExecCountProperty(ms map[string]int64, field, value string) { 98 collectNumericValue(ms, "cmd_"+field+"_calls", value) 99 100 if !p.collectedCommands[field] { 101 p.collectedCommands[field] = true 102 p.addCmdToCommandsCharts(field) 103 } 104 } 105 106 func (p *Pika) addCmdToCommandsCharts(cmd string) { 107 p.addDimToChart(chartCommandsCalls.ID, &module.Dim{ 108 ID: "cmd_" + cmd + "_calls", 109 Name: cmd, 110 Algo: module.Incremental, 111 }) 112 } 113 114 func (p *Pika) addDbToKeyspaceCharts(db string) { 115 p.addDimToChart(chartDbStringsKeys.ID, &module.Dim{ 116 ID: db + "_strings_keys", 117 Name: db, 118 }) 119 p.addDimToChart(chartDbStringsExpiresKeys.ID, &module.Dim{ 120 ID: db + "_strings_expires_keys", 121 Name: db, 122 }) 123 p.addDimToChart(chartDbStringsInvalidKeys.ID, &module.Dim{ 124 ID: db + "_strings_invalid_keys", 125 Name: db, 126 }) 127 128 p.addDimToChart(chartDbHashesKeys.ID, &module.Dim{ 129 ID: db + "_hashes_keys", 130 Name: db, 131 }) 132 p.addDimToChart(chartDbHashesExpiresKeys.ID, &module.Dim{ 133 ID: db + "_hashes_expires_keys", 134 Name: db, 135 }) 136 p.addDimToChart(chartDbHashesInvalidKeys.ID, &module.Dim{ 137 ID: db + "_hashes_invalid_keys", 138 Name: db, 139 }) 140 141 p.addDimToChart(chartDbListsKeys.ID, &module.Dim{ 142 ID: db + "_lists_keys", 143 Name: db, 144 }) 145 p.addDimToChart(chartDbListsExpiresKeys.ID, &module.Dim{ 146 ID: db + "_lists_expires_keys", 147 Name: db, 148 }) 149 p.addDimToChart(chartDbListsInvalidKeys.ID, &module.Dim{ 150 ID: db + "_lists_invalid_keys", 151 Name: db, 152 }) 153 154 p.addDimToChart(chartDbZsetsKeys.ID, &module.Dim{ 155 ID: db + "_zsets_keys", 156 Name: db, 157 }) 158 p.addDimToChart(chartDbZsetsExpiresKeys.ID, &module.Dim{ 159 ID: db + "_zsets_expires_keys", 160 Name: db, 161 }) 162 p.addDimToChart(chartDbZsetsInvalidKeys.ID, &module.Dim{ 163 ID: db + "_zsets_invalid_keys", 164 Name: db, 165 }) 166 167 p.addDimToChart(chartDbSetsKeys.ID, &module.Dim{ 168 ID: db + "_sets_keys", 169 Name: db, 170 }) 171 p.addDimToChart(chartDbSetsExpiresKeys.ID, &module.Dim{ 172 ID: db + "_sets_expires_keys", 173 Name: db, 174 }) 175 p.addDimToChart(chartDbSetsInvalidKeys.ID, &module.Dim{ 176 ID: db + "_sets_invalid_keys", 177 Name: db, 178 }) 179 } 180 181 func (p *Pika) addDimToChart(chartID string, dim *module.Dim) { 182 chart := p.Charts().Get(chartID) 183 if chart == nil { 184 p.Warningf("error on adding '%s' dimension: can not find '%s' chart", dim.ID, chartID) 185 return 186 } 187 if err := chart.AddDim(dim); err != nil { 188 p.Warning(err) 189 return 190 } 191 chart.MarkNotCreated() 192 } 193 194 func parseProperty(prop string) (field, value string, ok bool) { 195 var sep byte 196 if strings.HasPrefix(prop, "db") { 197 sep = ' ' 198 } else { 199 sep = ':' 200 } 201 i := strings.IndexByte(prop, sep) 202 if i == -1 { 203 return "", "", false 204 } 205 field, value = prop[:i], prop[i+1:] 206 return field, value, field != "" && value != "" 207 } 208 209 func collectNumericValue(ms map[string]int64, field, value string) { 210 v, err := strconv.ParseFloat(value, 64) 211 if err != nil { 212 return 213 } 214 if strings.IndexByte(value, '.') == -1 { 215 ms[field] = int64(v) 216 } else { 217 ms[field] = int64(v * precision) 218 } 219 }