| 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/netdata/go/plugins/plugin/framework/collectorapi" |
| 10 | commonmodel "github.com/prometheus/common/model" |
| 11 | ) |
| 12 | |
| 13 | const ( |
| 14 | prioDefault = collectorapi.Priority |
| 15 | prioGORuntime = prioDefault + 10 |
| 16 | ) |
| 17 | |
| 18 | // application is the "app" segment of a chart context: the configured Application, else |
| 19 | // the job Name. It selects the per-job chart-template namespace ("prometheus" when empty, |
| 20 | // else "prometheus.<app>"). |
| 21 | func (c *Collector) application() string { |
| 22 | if c.Application != "" { |
| 23 | return c.Application |
| 24 | } |
| 25 | return c.Name |
| 26 | } |
| 27 | |
| 28 | // getChartTitle derives a chart title (description) from the metric HELP, falling back to |
| 29 | // the metric name when HELP is absent. It is fed into the metrix instrument meta so |
| 30 | // chartengine autogen reproduces the V1 chart title. |
| 31 | func getChartTitle(name, help string) string { |
| 32 | if help == "" { |
| 33 | return fmt.Sprintf("Metric \"%s\"", name) |
| 34 | } |
| 35 | |
| 36 | help = strings.ReplaceAll(help, "'", "") |
| 37 | help = strings.TrimSuffix(help, ".") |
| 38 | |
| 39 | return help |
| 40 | } |
| 41 | |
| 42 | func getChartFamily(name string) (fam string) { |
| 43 | if strings.HasPrefix(name, "go_") { |
| 44 | return "go" |
| 45 | } |
| 46 | if strings.HasPrefix(name, "process_") { |
| 47 | return "process" |
| 48 | } |
| 49 | if parts := strings.SplitN(name, "_", 3); len(parts) < 3 { |
| 50 | fam = name |
| 51 | } else { |
| 52 | fam = parts[0] + "_" + parts[1] |
| 53 | } |
| 54 | |
| 55 | // remove number suffix if any |
| 56 | // load1, load5, load15 => load |
| 57 | i := len(fam) - 1 |
| 58 | for i >= 0 && fam[i] >= '0' && fam[i] <= '9' { |
| 59 | i-- |
| 60 | } |
| 61 | if i > 0 { |
| 62 | return fam[:i+1] |
| 63 | } |
| 64 | return fam |
| 65 | } |
| 66 | |
| 67 | func getChartUnits(name string) string { |
| 68 | // https://prometheus.io/docs/practices/naming/#metric-names |
| 69 | // ...must have a single unit (i.e. do not mix seconds with milliseconds, or seconds with bytes). |
| 70 | // ...should have a suffix describing the unit, in plural form. |
| 71 | // Note that an accumulating count has total as a suffix, in addition to the unit if applicable |
| 72 | |
| 73 | idx := strings.LastIndexByte(name, '_') |
| 74 | if idx == -1 { |
| 75 | // snmp_exporter: e.g. ifOutUcastPkts, ifOutOctets. |
| 76 | if idx = strings.LastIndexFunc(name, func(r rune) bool { return r >= 'A' && r <= 'Z' }); idx != -1 { |
| 77 | v := strings.ToLower(name[idx:]) |
| 78 | switch v { |
| 79 | case "pkts": |
| 80 | return "packets" |
| 81 | case "octets": |
| 82 | return "bytes" |
| 83 | case "mtu": |
| 84 | return "octets" |
| 85 | case "speed": |
| 86 | return "bits" |
| 87 | } |
| 88 | return v |
| 89 | } |
| 90 | return "events" |
| 91 | } |
| 92 | switch suffix := name[idx:]; suffix { |
| 93 | case "_total", "_sum", "_count", "_ratio": |
| 94 | return getChartUnits(name[:idx]) |
| 95 | } |
| 96 | switch units := name[idx+1:]; units { |
| 97 | case "hertz": |
| 98 | return "Hz" |
| 99 | default: |
| 100 | return units |
| 101 | } |
| 102 | } |
| 103 | |
| 104 | // instrumentUnit returns the chart unit metrix should carry for a family. V1 appends "/s" to summary |
| 105 | // quantile units (except seconds/time). chartengine autogen adds "/s" itself for the incremental |
| 106 | // counter/_sum routes but uses the unit as-is for the absolute summary-quantile route, so the writer |
| 107 | // must add it for summaries; gauges/counters/histograms pass the base unit. |
| 108 | func instrumentUnit(name string, typ commonmodel.MetricType) string { |
| 109 | unit := getChartUnits(name) |
| 110 | if typ == commonmodel.MetricTypeSummary { |
| 111 | switch unit { |
| 112 | case "seconds", "time": |
| 113 | default: |
| 114 | unit += "/s" |
| 115 | } |
| 116 | } |
| 117 | return unit |
| 118 | } |
| 119 | |
| 120 | func getChartPriority(name string) int { |
| 121 | if strings.HasPrefix(name, "go_") || strings.HasPrefix(name, "process_") { |
| 122 | return prioGORuntime |
| 123 | } |
| 124 | return prioDefault |
| 125 | } |