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  }