Skip to content

Commit c20ca98

Browse files
authored
Merge pull request #177 from Nexucis/feature/duration
add parameterizable duration to retrieve data from prometheus
2 parents eaf8dcb + 4fa3606 commit c20ca98

File tree

12 files changed

+171
-80
lines changed

12 files changed

+171
-80
lines changed

cmd/promql-langserver/promql-langserver.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
"log"
2121
"net/http"
2222
"os"
23+
"time"
2324

2425
kitlog "github.com/go-kit/kit/log"
2526
"github.com/prometheus-community/promql-langserver/config"
@@ -41,7 +42,7 @@ func main() {
4142
}
4243
if conf.RESTAPIPort != 0 {
4344
fmt.Fprintln(os.Stderr, "REST API: Listening on port ", conf.RESTAPIPort)
44-
prometheusClient, err := promClient.NewClient(conf.PrometheusURL)
45+
prometheusClient, err := promClient.NewClient(conf.PrometheusURL, time.Duration(conf.MetadataLookbackInterval))
4546
if err != nil {
4647
log.Fatal(err)
4748
}

config/config.go

+19-1
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,15 @@ import (
1818
"net/url"
1919
"os"
2020
"strconv"
21+
"time"
2122

2223
"github.com/kelseyhightower/envconfig"
24+
"github.com/prometheus/common/model"
2325
"gopkg.in/yaml.v3"
2426
)
2527

28+
const defaultInterval = model.Duration(12 * 3600 * time.Second)
29+
2630
// ReadConfig gets the GlobalConfig from a configFile (that is a path to the file).
2731
func ReadConfig(configFile string) (*Config, error) {
2832
if len(configFile) == 0 {
@@ -70,6 +74,8 @@ type Config struct {
7074
LogFormat LogFormat `yaml:"log_format"`
7175
PrometheusURL string `yaml:"prometheus_url"`
7276
RESTAPIPort uint64 `yaml:"rest_api_port"`
77+
// MetadataLookbackInterval is the time in second used to retrieve label and metrics from Prometheus
78+
MetadataLookbackInterval model.Duration `yaml:"metadata_lookback_interval"`
7379
}
7480

7581
// UnmarshalYAML overrides a function used internally by the yaml.v3 lib.
@@ -94,7 +100,8 @@ func (c *Config) unmarshalENV() error {
94100
PrometheusURL string
95101
// the envconfig lib is not able to convert an empty string to the value 0
96102
// so we have to convert it manually
97-
RESTAPIPort string
103+
RESTAPIPort string
104+
MetadataLookbackInterval string
98105
}{}
99106
if err := envconfig.Process(prefix, conf); err != nil {
100107
return err
@@ -106,6 +113,13 @@ func (c *Config) unmarshalENV() error {
106113
return parseError
107114
}
108115
}
116+
if len(conf.MetadataLookbackInterval) > 0 {
117+
var parseError error
118+
c.MetadataLookbackInterval, parseError = model.ParseDuration(conf.MetadataLookbackInterval)
119+
if parseError != nil {
120+
return parseError
121+
}
122+
}
109123
c.ActivateRPCLog = conf.ActivateRPCLog
110124
c.PrometheusURL = conf.PrometheusURL
111125
c.LogFormat = LogFormat(conf.LogFormat)
@@ -129,5 +143,9 @@ func (c *Config) Validate() error {
129143
c.LogFormat = TextFormat
130144
}
131145

146+
if c.MetadataLookbackInterval <= 0 {
147+
c.MetadataLookbackInterval = defaultInterval
148+
}
149+
132150
return nil
133151
}

config/config_test.go

+13-10
Original file line numberDiff line numberDiff line change
@@ -30,23 +30,26 @@ func TestUnmarshalENV(t *testing.T) {
3030
title: "empty config",
3131
variables: map[string]string{},
3232
expected: &Config{
33-
ActivateRPCLog: false,
34-
LogFormat: TextFormat,
33+
ActivateRPCLog: false,
34+
LogFormat: TextFormat,
35+
MetadataLookbackInterval: defaultInterval,
3536
},
3637
},
3738
{
3839
title: "full config",
3940
variables: map[string]string{
40-
"LANGSERVER_ACTIVATERPCLOG": "true",
41-
"LANGSERVER_PROMETHEUSURL": "http://localhost:9090",
42-
"LANGSERVER_RESTAPIPORT": "8080",
43-
"LANGSERVER_LOGFORMAT": "json",
41+
"LANGSERVER_ACTIVATERPCLOG": "true",
42+
"LANGSERVER_PROMETHEUSURL": "http://localhost:9090",
43+
"LANGSERVER_RESTAPIPORT": "8080",
44+
"LANGSERVER_LOGFORMAT": "json",
45+
"LANGSERVER_METADATALOOKBACKINTERVAL": "1w",
4446
},
4547
expected: &Config{
46-
ActivateRPCLog: true,
47-
PrometheusURL: "http://localhost:9090",
48-
RESTAPIPort: 8080,
49-
LogFormat: JSONFormat,
48+
ActivateRPCLog: true,
49+
PrometheusURL: "http://localhost:9090",
50+
RESTAPIPort: 8080,
51+
LogFormat: JSONFormat,
52+
MetadataLookbackInterval: 604800000000000,
5053
},
5154
},
5255
}

doc/developing_editor.md

+6-3
Original file line numberDiff line numberDiff line change
@@ -16,15 +16,17 @@ activate_rpc_log: false # It's a boolean in order to activate or deactivate the
1616
log_format: "text" # The format of the log printed. Possible value: json, text. Default value: "text"
1717
prometheus_url: "http://localhost:9090" # the HTTP URL of the prometheus server.
1818
rest_api_port: 8080 # When set, the server will be started as an HTTP server that provides a REST API instead of the language server protocol. Default value: 0
19+
metadata_lookback_interval: 2d # Interval used to retrieve data such as label and metrics from prometheus. Default value: 12h
1920
```
2021
2122
In case the file is not provided, it will read the configuration from the environment variables with the following structure:
2223
2324
```bash
2425
export LANGSERVER_ACTIVATERPCLOG="true"
2526
export LANGSERVER_PROMETHEUSURL="http://localhost:9090"
26-
export LANGSERVER_RESTAPIPORT"="8080"
27-
export LANGSERVER_LOGFORMAT"="json"
27+
export LANGSERVER_RESTAPIPORT="8080"
28+
export LANGSERVER_LOGFORMAT="json"
29+
export LANGSERVER_METADATALOOKBACKINTERVAL="1w"
2830
```
2931

3032
Note: documentation and default value are the same for both configuration (yaml and environment)
@@ -37,7 +39,8 @@ It has the following structure:
3739
```json
3840
{
3941
"promql": {
40-
"url": "http://localhost:9090" # the HTTP URL of the prometheus server.
42+
"url": "http://localhost:9090", # the HTTP URL of the prometheus server.
43+
"metadataLookbackInterval": "2h"
4144
}
4245
}
4346
```

langserver/completion.go

+2-3
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ import (
2020
"sort"
2121
"strconv"
2222
"strings"
23-
"time"
2423

2524
"github.com/pkg/errors"
2625

@@ -312,7 +311,7 @@ func (s *server) completeLabel(ctx context.Context, completions *[]protocol.Comp
312311
if vs != nil {
313312
metricName = vs.Name
314313
}
315-
allNames, err := s.metadataService.LabelNames(ctx, metricName, time.Now().Add(-100*time.Hour), time.Now())
314+
allNames, err := s.metadataService.LabelNames(ctx, metricName)
316315
if err != nil {
317316
// nolint: errcheck
318317
s.client.LogMessage(s.lifetime, &protocol.LogMessageParams{
@@ -355,7 +354,7 @@ OUTER:
355354

356355
// nolint: funlen
357356
func (s *server) completeLabelValue(ctx context.Context, completions *[]protocol.CompletionItem, location *cache.Location, labelName string) error {
358-
labelValues, err := s.metadataService.LabelValues(ctx, labelName, time.Now().Add(-100*time.Hour), time.Now())
357+
labelValues, err := s.metadataService.LabelValues(ctx, labelName)
359358
if err != nil {
360359
// nolint: errcheck
361360
s.client.LogMessage(s.lifetime, &protocol.LogMessageParams{

langserver/config.go

+69-28
Original file line numberDiff line numberDiff line change
@@ -16,48 +16,78 @@ package langserver
1616
import (
1717
"context"
1818
"fmt"
19+
"net/url"
20+
"time"
1921

2022
"github.com/prometheus-community/promql-langserver/internal/vendored/go-tools/lsp/protocol"
23+
"github.com/prometheus/common/model"
2124
)
2225

2326
// DidChangeConfiguration is required by the protocol.Server interface.
2427
func (s *server) DidChangeConfiguration(ctx context.Context, params *protocol.DidChangeConfigurationParams) error {
25-
langserverAddressConfigPath := []string{"promql", "url"}
28+
if params == nil {
29+
return nil
30+
}
31+
// nolint: errcheck
32+
s.client.LogMessage(
33+
s.lifetime,
34+
&protocol.LogMessageParams{
35+
Type: protocol.Info,
36+
Message: fmt.Sprintf("Received notification change: %v\n", params),
37+
})
2638

27-
if params != nil {
39+
setting := params.Settings
40+
41+
// the struct expected is the following
42+
// promql:
43+
// url: http://
44+
// interval: 3w
45+
m, ok := setting.(map[string]map[string]string)
46+
if !ok {
47+
// nolint: errcheck
48+
s.client.LogMessage(ctx, &protocol.LogMessageParams{
49+
Type: protocol.Error,
50+
Message: fmt.Sprint("unexpected format of the configuration"),
51+
})
52+
return nil
53+
}
54+
config, ok := m["promql"]
55+
if !ok {
2856
// nolint: errcheck
29-
s.client.LogMessage(
30-
s.lifetime,
31-
&protocol.LogMessageParams{
32-
Type: protocol.Info,
33-
Message: fmt.Sprintf("Received notification change: %v\n", params),
34-
})
57+
s.client.LogMessage(ctx, &protocol.LogMessageParams{
58+
Type: protocol.Error,
59+
Message: fmt.Sprint("promQL key not found"),
60+
})
61+
return nil
62+
}
3563

36-
setting := params.Settings
64+
if err := s.setURLFromChangeConfiguration(config); err != nil {
65+
// nolint: errcheck
66+
s.client.LogMessage(ctx, &protocol.LogMessageParams{
67+
Type: protocol.Info,
68+
Message: err.Error(),
69+
})
70+
}
3771

38-
for _, e := range langserverAddressConfigPath {
39-
m, ok := setting.(map[string]interface{})
40-
if !ok {
41-
break
42-
}
72+
if err := s.setMetadataLookbackInterval(config); err != nil {
73+
// nolint: errcheck
74+
s.client.LogMessage(ctx, &protocol.LogMessageParams{
75+
Type: protocol.Info,
76+
Message: err.Error(),
77+
})
78+
}
79+
return nil
80+
}
4381

44-
setting, ok = m[e]
45-
if !ok {
46-
break
47-
}
82+
func (s *server) setURLFromChangeConfiguration(settings map[string]string) error {
83+
if promURL, ok := settings["url"]; ok {
84+
if _, err := url.Parse(promURL); err != nil {
85+
return err
4886
}
49-
50-
if str, ok := setting.(string); ok {
51-
if err := s.connectPrometheus(str); err != nil {
52-
// nolint: errcheck
53-
s.client.LogMessage(ctx, &protocol.LogMessageParams{
54-
Type: protocol.Info,
55-
Message: err.Error(),
56-
})
57-
}
87+
if err := s.connectPrometheus(promURL); err != nil {
88+
return err
5889
}
5990
}
60-
6191
return nil
6292
}
6393

@@ -72,3 +102,14 @@ func (s *server) connectPrometheus(url string) error {
72102
}
73103
return nil
74104
}
105+
106+
func (s *server) setMetadataLookbackInterval(settings map[string]string) error {
107+
if interval, ok := settings["metadataLookbackInterval"]; ok {
108+
duration, err := model.ParseDuration(interval)
109+
if err != nil {
110+
return err
111+
}
112+
s.metadataService.SetLookbackInterval(time.Duration(duration))
113+
}
114+
return nil
115+
}

langserver/server.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"net"
2828
"os"
2929
"sync"
30+
"time"
3031

3132
"github.com/prometheus-community/promql-langserver/config"
3233
promClient "github.com/prometheus-community/promql-langserver/prometheus"
@@ -136,7 +137,7 @@ func ServerFromStream(ctx context.Context, stream jsonrpc2.Stream, conf *config.
136137

137138
// In order to have an error message in the IDE/editor, we are going to set the prometheusURL in the method server#Initialized.
138139
s.prometheusURL = conf.PrometheusURL
139-
prometheusClient, err := promClient.NewClient("")
140+
prometheusClient, err := promClient.NewClient("", time.Duration(conf.MetadataLookbackInterval))
140141
if err != nil {
141142
// nolint: errcheck
142143
s.client.ShowMessage(s.lifetime, &protocol.ShowMessageParams{

prometheus/compatible.go

+10-7
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import (
2525
type compatibleHTTPClient struct {
2626
MetadataService
2727
prometheusClient v1.API
28+
lookbackInterval time.Duration
2829
}
2930

3031
func (c *compatibleHTTPClient) MetricMetadata(ctx context.Context, metric string) (v1.Metadata, error) {
@@ -46,13 +47,12 @@ func (c *compatibleHTTPClient) AllMetricMetadata(ctx context.Context) (map[strin
4647
return c.prometheusClient.Metadata(ctx, "", "")
4748
}
4849

49-
func (c *compatibleHTTPClient) LabelNames(ctx context.Context, name string,
50-
startTime time.Time, endTime time.Time) ([]string, error) {
50+
func (c *compatibleHTTPClient) LabelNames(ctx context.Context, name string) ([]string, error) {
5151
if len(name) == 0 {
52-
names, _, err := c.prometheusClient.LabelNames(ctx, startTime, endTime)
52+
names, _, err := c.prometheusClient.LabelNames(ctx, time.Now().Add(-1*c.lookbackInterval), time.Now())
5353
return names, err
5454
}
55-
labelNames, _, err := c.prometheusClient.Series(ctx, []string{name}, startTime, endTime)
55+
labelNames, _, err := c.prometheusClient.Series(ctx, []string{name}, time.Now().Add(-1*c.lookbackInterval), time.Now())
5656
if err != nil {
5757
return nil, err
5858
}
@@ -70,16 +70,19 @@ func (c *compatibleHTTPClient) LabelNames(ctx context.Context, name string,
7070
return result, nil
7171
}
7272

73-
func (c *compatibleHTTPClient) LabelValues(ctx context.Context, label string,
74-
startTime time.Time, endTime time.Time) ([]model.LabelValue, error) {
75-
values, _, err := c.prometheusClient.LabelValues(ctx, label, startTime, endTime)
73+
func (c *compatibleHTTPClient) LabelValues(ctx context.Context, label string) ([]model.LabelValue, error) {
74+
values, _, err := c.prometheusClient.LabelValues(ctx, label, time.Now().Add(-1*c.lookbackInterval), time.Now())
7675
return values, err
7776
}
7877

7978
func (c *compatibleHTTPClient) ChangeDataSource(_ string) error {
8079
return fmt.Errorf("method not supported")
8180
}
8281

82+
func (c *compatibleHTTPClient) SetLookbackInterval(interval time.Duration) {
83+
c.lookbackInterval = interval
84+
}
85+
8386
func (c *compatibleHTTPClient) GetURL() string {
8487
return ""
8588
}

prometheus/empty.go

+6-2
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,22 @@ func (c *emptyHTTPClient) AllMetricMetadata(_ context.Context) (map[string][]v1.
3434
return make(map[string][]v1.Metadata), nil
3535
}
3636

37-
func (c *emptyHTTPClient) LabelNames(_ context.Context, _ string, _ time.Time, _ time.Time) ([]string, error) {
37+
func (c *emptyHTTPClient) LabelNames(_ context.Context, _ string) ([]string, error) {
3838
return []string{}, nil
3939
}
4040

41-
func (c *emptyHTTPClient) LabelValues(_ context.Context, _ string, _ time.Time, _ time.Time) ([]model.LabelValue, error) {
41+
func (c *emptyHTTPClient) LabelValues(_ context.Context, _ string) ([]model.LabelValue, error) {
4242
return []model.LabelValue{}, nil
4343
}
4444

4545
func (c *emptyHTTPClient) ChangeDataSource(_ string) error {
4646
return fmt.Errorf("method not supported")
4747
}
4848

49+
func (c *emptyHTTPClient) SetLookbackInterval(_ time.Duration) {
50+
51+
}
52+
4953
func (c *emptyHTTPClient) GetURL() string {
5054
return ""
5155
}

0 commit comments

Comments
 (0)