Skip to content

Commit

Permalink
Change GeneratorURL to use configurable Grafana explore URL (#8500)
Browse files Browse the repository at this point in the history
**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.
  • Loading branch information
Mohamed-Amine Bouqsimi authored Apr 21, 2023
1 parent 3ed5404 commit 62572b4
Show file tree
Hide file tree
Showing 5 changed files with 49 additions and 10 deletions.
6 changes: 5 additions & 1 deletion docs/sources/configuration/_index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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>]
Expand Down
2 changes: 1 addition & 1 deletion pkg/ruler/base/compat.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
41 changes: 37 additions & 4 deletions pkg/ruler/base/ruler.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package base

import (
"context"
"encoding/json"
"flag"
"fmt"
"hash/fnv"
Expand All @@ -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"

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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.")

Expand Down Expand Up @@ -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

Expand All @@ -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
Expand Down
8 changes: 5 additions & 3 deletions pkg/ruler/base/ruler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"math/rand"
"net/http"
"net/http/httptest"
"net/url"
"os"
"reflect"
"sort"
Expand Down Expand Up @@ -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
Expand All @@ -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),
},
},
},
Expand All @@ -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),
},
},
},
Expand All @@ -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...)
})
}
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/ruler/compat.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down

0 comments on commit 62572b4

Please sign in to comment.