master
go 96 lines 2.81 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 package prometheus
4
5 import (
6 "bytes"
7 "context"
8 "net/http"
9 "net/url"
10 "path/filepath"
11
12 "github.com/netdata/netdata/go/plugins/pkg/prometheus/selector"
13 "github.com/netdata/netdata/go/plugins/pkg/web"
14 )
15
16 type (
17 // Prometheus is a helper for scrape and parse prometheus format metrics.
18 Prometheus interface {
19 // ScrapeSeries and parse prometheus format metrics
20 ScrapeSeries() (Series, error)
21 Scrape() (MetricFamilies, error)
22 // ScrapeWithTransform scrapes, runs transform on every sample (a flat
23 // [Sample] stream before typed-family assembly, with the selector already
24 // applied), then assembles the kept, transformed samples into MetricFamilies.
25 // A nil transform behaves exactly like Scrape. The result aliases reused
26 // buffers, valid until the next scrape on this instance (same as Scrape).
27 //
28 // Samples reach transform in exposition order, with one exception: a
29 // _sum/_count sample whose family type is not yet known (its # TYPE, first
30 // bucket, or first quantile has not appeared) is deferred and delivered once
31 // the type resolves, or at end of scrape — so it may arrive after a later,
32 // unrelated sample. A per-sample (stateless) transform is unaffected.
33 ScrapeWithTransform(ctx context.Context, transform SampleTransform) (MetricFamilies, error)
34 HTTPClient() *http.Client
35 }
36
37 prometheus struct {
38 client *http.Client
39 src fetcher
40
41 parser promTextParser
42
43 buf *bytes.Buffer
44 }
45 )
46
47 // New creates a Prometheus instance.
48 func New(client *http.Client, request web.RequestConfig) Prometheus {
49 return NewWithSelector(client, request, nil)
50 }
51
52 // NewWithSelector creates a Prometheus instance with the selector.
53 func NewWithSelector(client *http.Client, request web.RequestConfig, sr selector.Selector) Prometheus {
54 p := &prometheus{
55 client: client,
56 buf: bytes.NewBuffer(make([]byte, 0, 16000)),
57 parser: promTextParser{sr: sr},
58 }
59
60 if v, err := url.Parse(request.URL); err == nil && v.Scheme == "file" {
61 p.src = &fileFetcher{path: filepath.Join(v.Host, v.Path)}
62 } else {
63 p.src = &httpFetcher{client: client, request: request}
64 }
65
66 return p
67 }
68
69 func (p *prometheus) HTTPClient() *http.Client {
70 return p.client
71 }
72
73 // ScrapeSeries scrapes metrics, parses and sorts
74 func (p *prometheus) ScrapeSeries() (Series, error) {
75 p.buf.Reset()
76
77 if err := p.src.fetch(context.Background(), p.buf); err != nil {
78 return nil, err
79 }
80
81 return p.parser.parseToSeries(p.buf.Bytes())
82 }
83
84 func (p *prometheus) Scrape() (MetricFamilies, error) {
85 return p.ScrapeWithTransform(context.Background(), nil)
86 }
87
88 func (p *prometheus) ScrapeWithTransform(ctx context.Context, transform SampleTransform) (MetricFamilies, error) {
89 p.buf.Reset()
90
91 if err := p.src.fetch(ctx, p.buf); err != nil {
92 return nil, err
93 }
94
95 return p.parser.parseToMetricFamilies(p.buf.Bytes(), transform)
96 }