github.com/netdata/go.d.plugin@v0.58.1/modules/prometheus/charts.go (about) 1 // SPDX-License-Identifier: GPL-3.0-or-later 2 3 package prometheus 4 5 import ( 6 "fmt" 7 "strings" 8 9 "github.com/netdata/go.d.plugin/agent/module" 10 "github.com/netdata/go.d.plugin/pkg/prometheus" 11 12 "github.com/prometheus/prometheus/model/labels" 13 ) 14 15 const ( 16 prioDefault = module.Priority 17 prioGORuntime = prioDefault + 10 18 ) 19 20 func (p *Prometheus) addGaugeChart(id, name, help string, labels labels.Labels) { 21 units := getChartUnits(name) 22 23 cType := module.Line 24 if strings.HasSuffix(units, "bytes") { 25 cType = module.Area 26 } 27 28 chart := &module.Chart{ 29 ID: id, 30 Title: getChartTitle(name, help), 31 Units: units, 32 Fam: getChartFamily(name), 33 Ctx: getChartContext(p.application(), name), 34 Type: cType, 35 Priority: getChartPriority(name), 36 Dims: module.Dims{ 37 {ID: id, Name: name, Div: precision}, 38 }, 39 } 40 41 for _, lbl := range labels { 42 chart.Labels = append(chart.Labels, 43 module.Label{Key: lbl.Name, Value: lbl.Value}, 44 ) 45 } 46 47 if err := p.Charts().Add(chart); err != nil { 48 p.Warning(err) 49 return 50 } 51 52 p.cache.addChart(id, chart) 53 } 54 55 func (p *Prometheus) addCounterChart(id, name, help string, labels labels.Labels) { 56 units := getChartUnits(name) 57 58 switch units { 59 case "seconds", "time": 60 default: 61 units += "/s" 62 } 63 64 cType := module.Line 65 if strings.HasSuffix(units, "bytes/s") { 66 cType = module.Area 67 } 68 69 chart := &module.Chart{ 70 ID: id, 71 Title: getChartTitle(name, help), 72 Units: units, 73 Fam: getChartFamily(name), 74 Ctx: getChartContext(p.application(), name), 75 Type: cType, 76 Priority: getChartPriority(name), 77 Dims: module.Dims{ 78 {ID: id, Name: name, Algo: module.Incremental, Div: precision}, 79 }, 80 } 81 for _, lbl := range labels { 82 chart.Labels = append(chart.Labels, 83 module.Label{Key: lbl.Name, Value: lbl.Value}, 84 ) 85 } 86 87 if err := p.Charts().Add(chart); err != nil { 88 p.Warning(err) 89 return 90 } 91 92 p.cache.addChart(id, chart) 93 } 94 95 func (p *Prometheus) addSummaryCharts(id, name, help string, labels labels.Labels, quantiles []prometheus.Quantile) { 96 units := getChartUnits(name) 97 98 switch units { 99 case "seconds", "time": 100 default: 101 units += "/s" 102 } 103 104 charts := module.Charts{ 105 { 106 ID: id, 107 Title: getChartTitle(name, help), 108 Units: units, 109 Fam: getChartFamily(name), 110 Ctx: getChartContext(p.application(), name), 111 Priority: getChartPriority(name), 112 Dims: func() (dims module.Dims) { 113 for _, v := range quantiles { 114 s := formatFloat(v.Quantile()) 115 dims = append(dims, &module.Dim{ 116 ID: fmt.Sprintf("%s_quantile=%s", id, s), 117 Name: fmt.Sprintf("quantile_%s", s), 118 Div: precision * precision, 119 }) 120 } 121 return dims 122 }(), 123 }, 124 { 125 ID: id + "_sum", 126 Title: getChartTitle(name, help), 127 Units: units, 128 Fam: getChartFamily(name), 129 Ctx: getChartContext(p.application(), name) + "_sum", 130 Priority: getChartPriority(name), 131 Dims: module.Dims{ 132 {ID: id + "_sum", Name: name + "_sum", Algo: module.Incremental, Div: precision}, 133 }, 134 }, 135 { 136 ID: id + "_count", 137 Title: getChartTitle(name, help), 138 Units: "events/s", 139 Fam: getChartFamily(name), 140 Ctx: getChartContext(p.application(), name) + "_count", 141 Priority: getChartPriority(name), 142 Dims: module.Dims{ 143 {ID: id + "_count", Name: name + "_count", Algo: module.Incremental}, 144 }, 145 }, 146 } 147 148 for _, chart := range charts { 149 for _, lbl := range labels { 150 chart.Labels = append(chart.Labels, module.Label{Key: lbl.Name, Value: lbl.Value}) 151 } 152 if err := p.Charts().Add(chart); err != nil { 153 p.Warning(err) 154 continue 155 } 156 p.cache.addChart(id, chart) 157 } 158 } 159 160 func (p *Prometheus) addHistogramCharts(id, name, help string, labels labels.Labels, buckets []prometheus.Bucket) { 161 units := getChartUnits(name) 162 163 switch units { 164 case "seconds", "time": 165 default: 166 units += "/s" 167 } 168 169 charts := module.Charts{ 170 { 171 ID: id, 172 Title: getChartTitle(name, help), 173 Units: "observations/s", 174 Fam: getChartFamily(name), 175 Ctx: getChartContext(p.application(), name), 176 Priority: getChartPriority(name), 177 Dims: func() (dims module.Dims) { 178 for _, v := range buckets { 179 s := formatFloat(v.UpperBound()) 180 dims = append(dims, &module.Dim{ 181 ID: fmt.Sprintf("%s_bucket=%s", id, s), 182 Name: fmt.Sprintf("bucket_%s", s), 183 Algo: module.Incremental, 184 }) 185 } 186 return dims 187 }(), 188 }, 189 { 190 ID: id + "_sum", 191 Title: getChartTitle(name, help), 192 Units: units, 193 Fam: getChartFamily(name), 194 Ctx: getChartContext(p.application(), name) + "_sum", 195 Priority: getChartPriority(name), 196 Dims: module.Dims{ 197 {ID: id + "_sum", Name: name + "_sum", Algo: module.Incremental, Div: precision}, 198 }, 199 }, 200 { 201 ID: id + "_count", 202 Title: getChartTitle(name, help), 203 Units: "events/s", 204 Fam: getChartFamily(name), 205 Ctx: getChartContext(p.application(), name) + "_count", 206 Priority: getChartPriority(name), 207 Dims: module.Dims{ 208 {ID: id + "_count", Name: name + "_count", Algo: module.Incremental}, 209 }, 210 }, 211 } 212 213 for _, chart := range charts { 214 for _, lbl := range labels { 215 chart.Labels = append(chart.Labels, module.Label{Key: lbl.Name, Value: lbl.Value}) 216 } 217 if err := p.Charts().Add(chart); err != nil { 218 p.Warning(err) 219 continue 220 } 221 p.cache.addChart(id, chart) 222 } 223 } 224 225 func (p *Prometheus) application() string { 226 if p.Application != "" { 227 return p.Application 228 } 229 return p.Name 230 } 231 232 func getChartTitle(name, help string) string { 233 if help == "" { 234 return fmt.Sprintf("Metric \"%s\"", name) 235 } 236 237 help = strings.Replace(help, "'", "", -1) 238 help = strings.TrimSuffix(help, ".") 239 240 return help 241 } 242 243 func getChartContext(app, name string) string { 244 if app == "" { 245 return fmt.Sprintf("prometheus.%s", name) 246 } 247 return fmt.Sprintf("prometheus.%s.%s", app, name) 248 } 249 250 func getChartFamily(metric string) (fam string) { 251 if strings.HasPrefix(metric, "go_") { 252 return "go" 253 } 254 if strings.HasPrefix(metric, "process_") { 255 return "process" 256 } 257 if parts := strings.SplitN(metric, "_", 3); len(parts) < 3 { 258 fam = metric 259 } else { 260 fam = parts[0] + "_" + parts[1] 261 } 262 263 // remove number suffix if any 264 // load1, load5, load15 => load 265 i := len(fam) - 1 266 for i >= 0 && fam[i] >= '0' && fam[i] <= '9' { 267 i-- 268 } 269 if i > 0 { 270 return fam[:i+1] 271 } 272 return fam 273 } 274 275 func getChartUnits(metric string) string { 276 // https://prometheus.io/docs/practices/naming/#metric-names 277 // ...must have a single unit (i.e. do not mix seconds with milliseconds, or seconds with bytes). 278 // ...should have a suffix describing the unit, in plural form. 279 // Note that an accumulating count has total as a suffix, in addition to the unit if applicable 280 281 idx := strings.LastIndexByte(metric, '_') 282 if idx == -1 { 283 return "events" 284 } 285 switch suffix := metric[idx:]; suffix { 286 case "_total", "_sum", "_count": 287 return getChartUnits(metric[:idx]) 288 } 289 switch units := metric[idx+1:]; units { 290 case "hertz": 291 return "Hz" 292 default: 293 return units 294 } 295 } 296 297 func getChartPriority(name string) int { 298 if strings.HasPrefix(name, "go_") || strings.HasPrefix(name, "process_") { 299 return prioGORuntime 300 } 301 return prioDefault 302 }