Change GeneratorURL to use configurable Grafana explore URL (#8500)

**What this PR does / why we need it**:
This PR changes the `GeneratorURL` associated with alerts generated by
Loki. The new `GeneratorURL` uses a Grafana URL path.
pull/9208/head^2
Mohamed-Amine Bouqsimi 2 years ago committed by GitHub
parent 3ed54048a5
commit 62572b4f83
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 6
      docs/sources/configuration/_index.md
  2. 2
      pkg/ruler/base/compat.go
  3. 41
      pkg/ruler/base/ruler.go
  4. 8
      pkg/ruler/base/ruler_test.go
  5. 2
      pkg/ruler/compat.go

@ -763,10 +763,14 @@ results_cache:
The `ruler` block configures the Loki ruler.
```yaml
# URL of alerts return path.
# Base URL of the Grafana instance.
# CLI flag: -ruler.external.url
[external_url: <url>]
# Datasource UID for the dashboard.
# CLI flag: -ruler.datasource-uid
[datasource_uid: <string> | default = ""]
# Labels to add to all alerts.
[external_labels: <list of Labels>]

@ -277,7 +277,7 @@ func DefaultTenantManagerFactory(cfg Config, p Pusher, q storage.Queryable, engi
QueryFunc: RecordAndReportRuleQueryMetrics(MetricsQueryFunc(EngineQueryFunc(engine, q, overrides, userID), totalQueries, failedQueries), queryTime, logger),
Context: user.InjectOrgID(ctx, userID),
ExternalURL: cfg.ExternalURL.URL,
NotifyFunc: SendAlerts(notifier, cfg.ExternalURL.URL.String()),
NotifyFunc: SendAlerts(notifier, cfg.ExternalURL.URL.String(), cfg.DatasourceUID),
Logger: log.With(logger, "user", userID),
Registerer: reg,
OutageTolerance: cfg.OutageTolerance,

@ -2,6 +2,7 @@ package base
import (
"context"
"encoding/json"
"flag"
"fmt"
"hash/fnv"
@ -28,7 +29,6 @@ import (
"github.com/prometheus/prometheus/model/rulefmt"
"github.com/prometheus/prometheus/notifier"
promRules "github.com/prometheus/prometheus/rules"
"github.com/prometheus/prometheus/util/strutil"
"github.com/weaveworks/common/user"
"golang.org/x/sync/errgroup"
@ -76,6 +76,8 @@ const (
type Config struct {
// This is used for template expansion in alerts; must be a valid URL.
ExternalURL flagext.URLValue `yaml:"external_url"`
// This is used for template expansion in alerts, and represents the corresponding Grafana datasource UID.
DatasourceUID string `yaml:"datasource_uid"`
// Labels to add to all alerts
ExternalLabels labels.Labels `yaml:"external_labels,omitempty" doc:"description=Labels to add to all alerts."`
// GRPC Client configuration.
@ -158,7 +160,8 @@ func (cfg *Config) RegisterFlags(f *flag.FlagSet) {
flagext.DeprecatedFlag(f, "ruler.num-workers", "This flag is no longer functional. For increased concurrency horizontal sharding is recommended", util_log.Logger)
cfg.ExternalURL.URL, _ = url.Parse("") // Must be non-nil
f.Var(&cfg.ExternalURL, "ruler.external.url", "URL of alerts return path.")
f.Var(&cfg.ExternalURL, "ruler.external.url", "Base URL of the Grafana instance.")
f.StringVar(&cfg.DatasourceUID, "ruler.datasource-uid", "", "Datasource UID for the dashboard.")
f.DurationVar(&cfg.EvaluationInterval, "ruler.evaluation-interval", 1*time.Minute, "How frequently to evaluate rules.")
f.DurationVar(&cfg.PollInterval, "ruler.poll-interval", 1*time.Minute, "How frequently to poll for rule changes.")
@ -374,11 +377,41 @@ type sender interface {
Send(alerts ...*notifier.Alert)
}
type query struct {
Expr string `json:"expr"`
QueryType string `json:"queryType"`
Datasource *datasource `json:"datasource,omitempty"`
}
type datasource struct {
Type string `json:"type"`
UID string `json:"uid"`
}
func grafanaLinkForExpression(expr, datasourceUID string) string {
exprStruct := query{
Expr: expr,
QueryType: "range",
}
if datasourceUID != "" {
exprStruct.Datasource = &datasource{
Type: "loki",
UID: datasourceUID,
}
}
marshaledExpression, _ := json.Marshal(exprStruct)
escapedExpression := url.QueryEscape(string(marshaledExpression))
str := `/explore?left={"queries":[%s]}`
return fmt.Sprintf(str, escapedExpression)
}
// SendAlerts implements a rules.NotifyFunc for a Notifier.
// It filters any non-firing alerts from the input.
//
// Copied from Prometheus's main.go.
func SendAlerts(n sender, externalURL string) promRules.NotifyFunc {
func SendAlerts(n sender, externalURL, datasourceUID string) promRules.NotifyFunc {
return func(ctx context.Context, expr string, alerts ...*promRules.Alert) {
var res []*notifier.Alert
@ -387,7 +420,7 @@ func SendAlerts(n sender, externalURL string) promRules.NotifyFunc {
StartsAt: alert.FiredAt,
Labels: alert.Labels,
Annotations: alert.Annotations,
GeneratorURL: externalURL + strutil.TableLinkForExpression(expr),
GeneratorURL: externalURL + grafanaLinkForExpression(expr, datasourceUID),
}
if !alert.ResolvedAt.IsZero() {
a.EndsAt = alert.ResolvedAt

@ -7,6 +7,7 @@ import (
"math/rand"
"net/http"
"net/http/httptest"
"net/url"
"os"
"reflect"
"sort"
@ -1645,6 +1646,7 @@ func (s senderFunc) Send(alerts ...*notifier.Alert) {
}
func TestSendAlerts(t *testing.T) {
escapedExpression := url.QueryEscape("{\"expr\":\"up\",\"queryType\":\"range\",\"datasource\":{\"type\":\"loki\",\"uid\":\"uid\"}}")
testCases := []struct {
in []*promRules.Alert
exp []*notifier.Alert
@ -1665,7 +1667,7 @@ func TestSendAlerts(t *testing.T) {
Annotations: []labels.Label{{Name: "a2", Value: "v2"}},
StartsAt: time.Unix(2, 0),
EndsAt: time.Unix(3, 0),
GeneratorURL: "http://localhost:9090/graph?g0.expr=up&g0.tab=1",
GeneratorURL: fmt.Sprintf("http://localhost:8080/explore?left={\"queries\":[%s]}", escapedExpression),
},
},
},
@ -1685,7 +1687,7 @@ func TestSendAlerts(t *testing.T) {
Annotations: []labels.Label{{Name: "a2", Value: "v2"}},
StartsAt: time.Unix(2, 0),
EndsAt: time.Unix(4, 0),
GeneratorURL: "http://localhost:9090/graph?g0.expr=up&g0.tab=1",
GeneratorURL: fmt.Sprintf("http://localhost:8080/explore?left={\"queries\":[%s]}", escapedExpression),
},
},
},
@ -1703,7 +1705,7 @@ func TestSendAlerts(t *testing.T) {
}
require.Equal(t, tc.exp, alerts)
})
SendAlerts(senderFunc, "http://localhost:9090")(context.TODO(), "up", tc.in...)
SendAlerts(senderFunc, "http://localhost:8080", "uid")(context.TODO(), "up", tc.in...)
})
}
}

@ -157,7 +157,7 @@ func MultiTenantRuleManager(cfg Config, evaluator Evaluator, overrides RulesLimi
QueryFunc: queryFn,
Context: user.InjectOrgID(ctx, userID),
ExternalURL: cfg.ExternalURL.URL,
NotifyFunc: ruler.SendAlerts(notifier, cfg.ExternalURL.URL.String()),
NotifyFunc: ruler.SendAlerts(notifier, cfg.ExternalURL.URL.String(), cfg.DatasourceUID),
Logger: logger,
Registerer: reg,
OutageTolerance: cfg.OutageTolerance,

Loading…
Cancel
Save