Skip to content

Commit a492451

Browse files
authored
Merge pull request #164 from Nexucis/feature/change-prometheus-client
Update prometheus client implementation
2 parents 3992a10 + d2054af commit a492451

File tree

8 files changed

+63
-57
lines changed

8 files changed

+63
-57
lines changed

langserver/completion.go

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"sort"
2121
"strconv"
2222
"strings"
23+
"time"
2324

2425
"github.com/pkg/errors"
2526

@@ -94,7 +95,7 @@ func (s *server) Completion(ctx context.Context, params *protocol.CompletionPara
9495
}
9596

9697
func (s *server) completeMetricName(ctx context.Context, completions *[]protocol.CompletionItem, location *cache.Location, metricName string) error {
97-
allMetadata, err := s.prometheusClient.AllMetadata(ctx)
98+
allMetadata, err := s.metadataService.AllMetricMetadata(ctx)
9899
if err != nil {
99100
return err
100101
}
@@ -311,7 +312,7 @@ func (s *server) completeLabel(ctx context.Context, completions *[]protocol.Comp
311312
if vs != nil {
312313
metricName = vs.Name
313314
}
314-
allNames, err := s.prometheusClient.LabelNames(ctx, metricName)
315+
allNames, err := s.metadataService.LabelNames(ctx, metricName, time.Now().Add(-100*time.Hour), time.Now())
315316
if err != nil {
316317
// nolint: errcheck
317318
s.client.LogMessage(s.lifetime, &protocol.LogMessageParams{
@@ -327,14 +328,8 @@ func (s *server) completeLabel(ctx context.Context, completions *[]protocol.Comp
327328
}
328329

329330
labelName := location.Node.(*promql.Item).Val
330-
var prevMatched string
331331
OUTER:
332332
for _, match := range getMatches(labelName, allNames) {
333-
// Skip duplicates
334-
if prevMatched == match.Str {
335-
continue
336-
}
337-
prevMatched = match.Str
338333
// Skip labels that already have matchers
339334
if vs != nil {
340335
for _, m := range vs.LabelMatchers {
@@ -360,7 +355,7 @@ OUTER:
360355

361356
// nolint: funlen
362357
func (s *server) completeLabelValue(ctx context.Context, completions *[]protocol.CompletionItem, location *cache.Location, labelName string) error {
363-
labelValues, err := s.prometheusClient.LabelValues(ctx, labelName)
358+
labelValues, err := s.metadataService.LabelValues(ctx, labelName)
364359
if err != nil {
365360
// nolint: errcheck
366361
s.client.LogMessage(s.lifetime, &protocol.LogMessageParams{

langserver/hover.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ func (s *server) nodeToDocMarkdown(ctx context.Context, location *cache.Location
138138
}
139139
}
140140

141-
promURL := s.prometheusClient.GetURL()
141+
promURL := s.metadataService.GetURL()
142142

143143
if promURL != "" && !s.headless {
144144
loc := *location
@@ -195,7 +195,7 @@ func (s *server) getMetricDocs(ctx context.Context, metric string) (string, erro
195195
var ret strings.Builder
196196
ret.WriteString(fmt.Sprintf("### %s\n\n", metric))
197197

198-
metadata, err := s.prometheusClient.Metadata(ctx, metric)
198+
metadata, err := s.metadataService.MetricMetadata(ctx, metric)
199199
if err != nil {
200200
return "", err
201201
}

langserver/server.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ type server struct {
5656

5757
config *Config
5858

59-
prometheusClient promClient.Client
59+
metadataService promClient.MetadataService
6060

6161
lifetime context.Context
6262
exit func()
@@ -80,12 +80,12 @@ func (s Server) Run() error {
8080
// CreateHeadlessServer creates a locked down server instance for the REST API.
8181
//
8282
// "locked down" in this case means, that the instance cannot send or receive any JSONRPC communication. Logging messages that the instance tries to send over JSONRPC are redirected to stderr.
83-
func CreateHeadlessServer(ctx context.Context, prometheusClient promClient.Client, logger log.Logger) (HeadlessServer, error) {
83+
func CreateHeadlessServer(ctx context.Context, metadataService promClient.MetadataService, logger log.Logger) (HeadlessServer, error) {
8484
s := &server{
85-
client: &headlessClient{logger: logger},
86-
headless: true,
87-
config: &Config{PrometheusURL: prometheusClient.GetURL()},
88-
prometheusClient: prometheusClient,
85+
client: &headlessClient{logger: logger},
86+
headless: true,
87+
config: &Config{PrometheusURL: metadataService.GetURL()},
88+
metadataService: metadataService,
8989
}
9090

9191
s.lifetime, s.exit = context.WithCancel(ctx)
@@ -130,13 +130,13 @@ func ServerFromStream(ctx context.Context, stream jsonrpc2.Stream, config *Confi
130130
})
131131
panic(err)
132132
}
133-
s.prometheusClient = prometheusClient
133+
s.metadataService = prometheusClient
134134

135135
return ctx, Server{s}
136136
}
137137

138138
func (s *server) connectPrometheus(url string) error {
139-
if err := s.prometheusClient.ChangeDataSource(url); err != nil {
139+
if err := s.metadataService.ChangeDataSource(url); err != nil {
140140
// nolint: errcheck
141141
s.client.ShowMessage(s.lifetime, &protocol.ShowMessageParams{
142142
Type: protocol.Error,

prometheus/compatible.go

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ import (
2323

2424
// compatibleHTTPClient must be used to contact a distant prometheus with a version >= v2.15.
2525
type compatibleHTTPClient struct {
26-
Client
26+
MetadataService
2727
prometheusClient v1.API
2828
}
2929

30-
func (c *compatibleHTTPClient) Metadata(ctx context.Context, metric string) (v1.Metadata, error) {
30+
func (c *compatibleHTTPClient) MetricMetadata(ctx context.Context, metric string) (v1.Metadata, error) {
3131
metadata, err := c.prometheusClient.Metadata(ctx, metric, "1")
3232
if err != nil {
3333
return v1.Metadata{}, err
@@ -42,25 +42,30 @@ func (c *compatibleHTTPClient) Metadata(ctx context.Context, metric string) (v1.
4242
}, nil
4343
}
4444

45-
func (c *compatibleHTTPClient) AllMetadata(ctx context.Context) (map[string][]v1.Metadata, error) {
45+
func (c *compatibleHTTPClient) AllMetricMetadata(ctx context.Context) (map[string][]v1.Metadata, error) {
4646
return c.prometheusClient.Metadata(ctx, "", "")
4747
}
4848

49-
func (c *compatibleHTTPClient) LabelNames(ctx context.Context, name string) ([]string, error) {
49+
func (c *compatibleHTTPClient) LabelNames(ctx context.Context, name string, startTime time.Time, endTime time.Time) ([]string, error) {
5050
if len(name) == 0 {
5151
names, _, err := c.prometheusClient.LabelNames(ctx)
5252
return names, err
5353
}
54-
labelNames, _, err := c.prometheusClient.Series(ctx, []string{name}, time.Now().Add(-100*time.Hour), time.Now())
54+
labelNames, _, err := c.prometheusClient.Series(ctx, []string{name}, startTime, endTime)
5555
if err != nil {
5656
return nil, err
5757
}
58-
var result []string
58+
// subResult is used as a set of label. Like that we are sure we don't have any duplication
59+
subResult := make(map[string]bool)
5960
for _, ln := range labelNames {
6061
for l := range ln {
61-
result = append(result, string(l))
62+
subResult[string(l)] = true
6263
}
6364
}
65+
result := make([]string, 0, len(subResult))
66+
for l := range subResult {
67+
result = append(result, l)
68+
}
6469
return result, nil
6570
}
6671

prometheus/empty.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,25 +15,26 @@ package prometheus
1515
import (
1616
"context"
1717
"fmt"
18+
"time"
1819

1920
v1 "github.com/prometheus/client_golang/api/prometheus/v1"
2021
"github.com/prometheus/common/model"
2122
)
2223

2324
// emptyHTTPClient must be used when no prometheus URL has been defined.
2425
type emptyHTTPClient struct {
25-
Client
26+
MetadataService
2627
}
2728

28-
func (c *emptyHTTPClient) Metadata(_ context.Context, _ string) (v1.Metadata, error) {
29+
func (c *emptyHTTPClient) MetricMetadata(_ context.Context, _ string) (v1.Metadata, error) {
2930
return v1.Metadata{}, nil
3031
}
3132

32-
func (c *emptyHTTPClient) AllMetadata(_ context.Context) (map[string][]v1.Metadata, error) {
33+
func (c *emptyHTTPClient) AllMetricMetadata(_ context.Context) (map[string][]v1.Metadata, error) {
3334
return make(map[string][]v1.Metadata), nil
3435
}
3536

36-
func (c *emptyHTTPClient) LabelNames(_ context.Context, _ string) ([]string, error) {
37+
func (c *emptyHTTPClient) LabelNames(_ context.Context, _ string, _ time.Time, _ time.Time) ([]string, error) {
3738
return []string{}, nil
3839
}
3940

prometheus/client.go renamed to prometheus/metadata_service.go

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -84,15 +84,15 @@ type buildInfoData struct {
8484
GoVersion string `json:"goVersion"`
8585
}
8686

87-
// Client is a light prometheus client used by LSP to get data from a a prometheus Server.
88-
type Client interface {
89-
// Metadata returns the first occurrence of metadata about metrics currently scraped by the metric name.
90-
Metadata(ctx context.Context, metric string) (v1.Metadata, error)
91-
// AllMetadata returns metadata about metrics currently scraped for all existing metrics.
92-
AllMetadata(ctx context.Context) (map[string][]v1.Metadata, error)
87+
// MetadataService is a light prometheus client used by LSP to get metric or label information from prometheus Server.
88+
type MetadataService interface {
89+
// MetricMetadata returns the first occurrence of metadata about metrics currently scraped by the metric name.
90+
MetricMetadata(ctx context.Context, metric string) (v1.Metadata, error)
91+
// AllMetricMetadata returns metadata about metrics currently scraped for all existing metrics.
92+
AllMetricMetadata(ctx context.Context) (map[string][]v1.Metadata, error)
9393
// LabelNames returns all the unique label names present in the block in sorted order.
9494
// If a metric is provided, then it will return all unique label names linked to the metric during a predefined period of time
95-
LabelNames(ctx context.Context, metricName string) ([]string, error)
95+
LabelNames(ctx context.Context, metricName string, startTime time.Time, endTime time.Time) ([]string, error)
9696
// LabelValues performs a query for the values of the given label.
9797
LabelValues(ctx context.Context, label string) ([]model.LabelValue, error)
9898
// ChangeDataSource is used if the prometheusURL is changing.
@@ -107,14 +107,14 @@ type Client interface {
107107
// You should use this instance directly and not the other one (compatibleHTTPClient and notCompatibleHTTPClient)
108108
// because it will manage which sub instance of the Client to use (like a factory).
109109
type httpClient struct {
110-
Client
110+
MetadataService
111111
requestTimeout time.Duration
112112
mutex sync.RWMutex
113-
subClient Client
113+
subClient MetadataService
114114
url string
115115
}
116116

117-
func NewClient(prometheusURL string) (Client, error) {
117+
func NewClient(prometheusURL string) (MetadataService, error) {
118118
c := &httpClient{
119119
requestTimeout: 30,
120120
}
@@ -124,22 +124,22 @@ func NewClient(prometheusURL string) (Client, error) {
124124
return c, nil
125125
}
126126

127-
func (c *httpClient) Metadata(ctx context.Context, metric string) (v1.Metadata, error) {
127+
func (c *httpClient) MetricMetadata(ctx context.Context, metric string) (v1.Metadata, error) {
128128
c.mutex.RLock()
129129
defer c.mutex.RUnlock()
130-
return c.subClient.Metadata(ctx, metric)
130+
return c.subClient.MetricMetadata(ctx, metric)
131131
}
132132

133-
func (c *httpClient) AllMetadata(ctx context.Context) (map[string][]v1.Metadata, error) {
133+
func (c *httpClient) AllMetricMetadata(ctx context.Context) (map[string][]v1.Metadata, error) {
134134
c.mutex.RLock()
135135
defer c.mutex.RUnlock()
136-
return c.subClient.AllMetadata(ctx)
136+
return c.subClient.AllMetricMetadata(ctx)
137137
}
138138

139-
func (c *httpClient) LabelNames(ctx context.Context, name string) ([]string, error) {
139+
func (c *httpClient) LabelNames(ctx context.Context, name string, startTime time.Time, endTime time.Time) ([]string, error) {
140140
c.mutex.RLock()
141141
defer c.mutex.RUnlock()
142-
return c.subClient.LabelNames(ctx, name)
142+
return c.subClient.LabelNames(ctx, name, startTime, endTime)
143143
}
144144

145145
func (c *httpClient) LabelValues(ctx context.Context, label string) ([]model.LabelValue, error) {

prometheus/not_compatible.go

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,11 @@ import (
2323

2424
// notCompatibleHTTPClient must be used to contact a distant prometheus with a version < v2.15.
2525
type notCompatibleHTTPClient struct {
26-
Client
26+
MetadataService
2727
prometheusClient v1.API
2828
}
2929

30-
func (c *notCompatibleHTTPClient) Metadata(ctx context.Context, metric string) (v1.Metadata, error) {
30+
func (c *notCompatibleHTTPClient) MetricMetadata(ctx context.Context, metric string) (v1.Metadata, error) {
3131
metadata, err := c.prometheusClient.TargetsMetadata(ctx, "", metric, "1")
3232
if err != nil {
3333
return v1.Metadata{}, err
@@ -42,7 +42,7 @@ func (c *notCompatibleHTTPClient) Metadata(ctx context.Context, metric string) (
4242
}, nil
4343
}
4444

45-
func (c *notCompatibleHTTPClient) AllMetadata(ctx context.Context) (map[string][]v1.Metadata, error) {
45+
func (c *notCompatibleHTTPClient) AllMetricMetadata(ctx context.Context) (map[string][]v1.Metadata, error) {
4646
metricNames, _, err := c.prometheusClient.LabelValues(ctx, "__name__")
4747
if err != nil {
4848
return nil, err
@@ -54,21 +54,26 @@ func (c *notCompatibleHTTPClient) AllMetadata(ctx context.Context) (map[string][
5454
return allMetadata, nil
5555
}
5656

57-
func (c *notCompatibleHTTPClient) LabelNames(ctx context.Context, name string) ([]string, error) {
57+
func (c *notCompatibleHTTPClient) LabelNames(ctx context.Context, name string, startTime time.Time, endTime time.Time) ([]string, error) {
5858
if len(name) == 0 {
5959
names, _, err := c.prometheusClient.LabelNames(ctx)
6060
return names, err
6161
}
62-
labelNames, _, err := c.prometheusClient.Series(ctx, []string{name}, time.Now().Add(-100*time.Hour), time.Now())
62+
labelNames, _, err := c.prometheusClient.Series(ctx, []string{name}, startTime, endTime)
6363
if err != nil {
6464
return nil, err
6565
}
66-
var result []string
66+
// subResult is used as a set of label. Like that we are sure we don't have any duplication
67+
subResult := make(map[string]bool)
6768
for _, ln := range labelNames {
6869
for l := range ln {
69-
result = append(result, string(l))
70+
subResult[string(l)] = true
7071
}
7172
}
73+
result := make([]string, 0, len(subResult))
74+
for l := range subResult {
75+
result = append(result, l)
76+
}
7277
return result, nil
7378
}
7479

rest/handler.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,16 @@ import (
3535
//
3636
// Expects a prometheus Client as a second argument.
3737
// The provided Logger should be synchronized.
38-
func CreateHandler(ctx context.Context, prometheusClient promClient.Client, logger log.Logger) (http.Handler, error) {
39-
return CreateInstHandler(ctx, prometheusClient, nil, logger)
38+
func CreateHandler(ctx context.Context, metadataService promClient.MetadataService, logger log.Logger) (http.Handler, error) {
39+
return CreateInstHandler(ctx, metadataService, nil, logger)
4040
}
4141

4242
// CreateInstHandler creates an instrumented http.Handler for the PromQL langserver REST API.
4343
//
4444
// Expects a prometheus Client as a second argument and a Registry as third argument.
4545
// The provided Logger should be synchronized.
46-
func CreateInstHandler(ctx context.Context, prometheusClient promClient.Client, r *prometheus.Registry, logger log.Logger) (http.Handler, error) {
47-
lgs, err := langserver.CreateHeadlessServer(ctx, prometheusClient, logger)
46+
func CreateInstHandler(ctx context.Context, metadataService promClient.MetadataService, r *prometheus.Registry, logger log.Logger) (http.Handler, error) {
47+
lgs, err := langserver.CreateHeadlessServer(ctx, metadataService, logger)
4848
if err != nil {
4949
return nil, err
5050
}

0 commit comments

Comments
 (0)