Skip to content

Commit

Permalink
Ruler: Add alert source template (thanos-io#6308)
Browse files Browse the repository at this point in the history
* Add alert source template in rule

Signed-off-by: Zhuoyuan Liu <[email protected]>

* Validate template in start phase

Signed-off-by: Zhuoyuan Liu <[email protected]>

* Move the start check to runrule

Signed-off-by: Zhuoyuan Liu <[email protected]>

* move the flag to config.go

Signed-off-by: Zhuoyuan Liu <[email protected]>

* Updates the docs

Signed-off-by: Zhuoyuan Liu <[email protected]>

* Add test for validateTemplate

Signed-off-by: Zhuoyuan Liu <[email protected]>

* Add new test case

Signed-off-by: Zhuoyuan Liu <[email protected]>

* Remove unnecessary variable

Signed-off-by: Zhuoyuan Liu <[email protected]>

* Add changelogs

Signed-off-by: Zhuoyuan Liu <[email protected]>

* Update CHANGELOG.md

Signed-off-by: Matej Gera <[email protected]>

---------

Signed-off-by: Zhuoyuan Liu <[email protected]>
Signed-off-by: Matej Gera <[email protected]>
Co-authored-by: Matej Gera <[email protected]>
  • Loading branch information
2 people authored and coleenquadros committed Sep 18, 2023
1 parent 1c6bdb8 commit 63cd7a1
Show file tree
Hide file tree
Showing 5 changed files with 109 additions and 2 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ We use *breaking :warning:* to mark changes that are not backward compatible (re
### Added

- [#6605](https://github.com/thanos-io/thanos/pull/6605) Query Frontend: Support vertical sharding binary expression with metric name when no matching labels specified.
- [#6308](https://github.com/thanos-io/thanos/pull/6308) Ruler: Support configuration flag that allows customizing template for alert message.

### Changed

Expand Down
3 changes: 3 additions & 0 deletions cmd/thanos/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ type alertMgrConfig struct {
alertExcludeLabels []string
alertQueryURL *string
alertRelabelConfigPath *extflag.PathOrContent
alertSourceTemplate *string
}

func (ac *alertMgrConfig) registerFlag(cmd extflag.FlagClause) *alertMgrConfig {
Expand All @@ -234,5 +235,7 @@ func (ac *alertMgrConfig) registerFlag(cmd extflag.FlagClause) *alertMgrConfig {
cmd.Flag("alert.label-drop", "Labels by name to drop before sending to alertmanager. This allows alert to be deduplicated on replica label (repeated). Similar Prometheus alert relabelling").
StringsVar(&ac.alertExcludeLabels)
ac.alertRelabelConfigPath = extflag.RegisterPathOrContent(cmd, "alert.relabel-config", "YAML file that contains alert relabelling configuration.", extflag.WithEnvSubstitution())
ac.alertSourceTemplate = cmd.Flag("alert.query-template", "Template to use in alerts source field. Need only include {{.Expr}} parameter").Default("/graph?g0.expr={{.Expr}}&g0.tab=1").String()

return ac
}
48 changes: 46 additions & 2 deletions cmd/thanos/rule.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
package main

import (
"bytes"
"context"
"fmt"
"html/template"
"math/rand"
"net/http"
"net/url"
Expand Down Expand Up @@ -38,7 +41,6 @@ import (
"github.com/prometheus/prometheus/tsdb"
"github.com/prometheus/prometheus/tsdb/agent"
"github.com/prometheus/prometheus/tsdb/wlog"
"github.com/prometheus/prometheus/util/strutil"

"github.com/thanos-io/objstore"
"github.com/thanos-io/objstore/client"
Expand Down Expand Up @@ -101,6 +103,10 @@ type ruleConfig struct {
storeRateLimits store.SeriesSelectLimits
}

type Expression struct {
Expr string
}

func (rc *ruleConfig) registerFlag(cmd extkingpin.FlagClause) {
rc.http.registerFlag(cmd)
rc.grpc.registerFlag(cmd)
Expand Down Expand Up @@ -329,6 +335,10 @@ func runRule(
}
}

if err := validateTemplate(*conf.alertmgr.alertSourceTemplate); err != nil {
return errors.Wrap(err, "invalid alert source template")
}

queryProvider := dns.NewProvider(
logger,
extprom.WrapRegistererWithPrefix("thanos_rule_query_apis_", reg),
Expand Down Expand Up @@ -492,11 +502,15 @@ func runRule(
if alrt.State == rules.StatePending {
continue
}
expressionURL, err := tableLinkForExpression(*conf.alertmgr.alertSourceTemplate, expr)
if err != nil {
level.Warn(logger).Log("msg", "failed to generate link for expression", "expr", expr, "err", err)
}
a := &notifier.Alert{
StartsAt: alrt.FiredAt,
Labels: alrt.Labels,
Annotations: alrt.Annotations,
GeneratorURL: conf.alertQueryURL.String() + strutil.TableLinkForExpression(expr),
GeneratorURL: conf.alertQueryURL.String() + expressionURL,
}
if !alrt.ResolvedAt.IsZero() {
a.EndsAt = alrt.ResolvedAt
Expand Down Expand Up @@ -934,3 +948,33 @@ func reloadRules(logger log.Logger,
}
return errs.Err()
}

func tableLinkForExpression(tmpl string, expr string) (string, error) {
// template example: "/graph?g0.expr={{.Expr}}&g0.tab=1"
escapedExpression := url.QueryEscape(expr)

escapedExpr := Expression{Expr: escapedExpression}
t, err := template.New("url").Parse(tmpl)
if err != nil {
return "", errors.Wrap(err, "failed to parse template")
}

var buf bytes.Buffer
if err := t.Execute(&buf, escapedExpr); err != nil {
return "", errors.Wrap(err, "failed to execute template")
}
return buf.String(), nil
}

func validateTemplate(tmplStr string) error {
tmpl, err := template.New("test").Parse(tmplStr)
if err != nil {
return fmt.Errorf("failed to parse the template: %w", err)
}
var buf bytes.Buffer
err = tmpl.Execute(&buf, Expression{Expr: "test_expr"})
if err != nil {
return fmt.Errorf("failed to execute the template: %w", err)
}
return nil
}
56 changes: 56 additions & 0 deletions cmd/thanos/rule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,59 @@ func Test_parseFlagLabels(t *testing.T) {
testutil.Equals(t, err != nil, td.expectErr)
}
}

func Test_validateTemplate(t *testing.T) {
tData := []struct {
template string
expectErr bool
}{
{
template: `/graph?g0.expr={{.Expr}}&g0.tab=1`,
expectErr: false,
},
{
template: `/graph?g0.expr={{.Expression}}&g0.tab=1`,
expectErr: true,
},
{
template: `another template includes {{.Expr}}`,
expectErr: false,
},
}
for _, td := range tData {
err := validateTemplate(td.template)
testutil.Equals(t, err != nil, td.expectErr)
}
}

func Test_tableLinkForExpression(t *testing.T) {
tData := []struct {
template string
expr string
expectStr string
expectErr bool
}{
{
template: `/graph?g0.expr={{.Expr}}&g0.tab=1`,
expr: `up{app="foo"}`,
expectStr: `/graph?g0.expr=up%7Bapp%3D%22foo%22%7D&g0.tab=1`,
expectErr: false,
},
{
template: `/graph?g0.expr={{.Expression}}&g0.tab=1`,
expr: "test_expr",
expectErr: true,
},
{
template: `another template includes {{.Expr}}`,
expr: "test_expr",
expectStr: `another template includes test_expr`,
expectErr: false,
},
}
for _, td := range tData {
resStr, err := tableLinkForExpression(td.template, td.expr)
testutil.Equals(t, err != nil, td.expectErr)
testutil.Equals(t, resStr, td.expectStr)
}
}
3 changes: 3 additions & 0 deletions docs/components/rule.md
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,9 @@ Flags:
to alertmanager. This allows alert to be
deduplicated on replica label (repeated).
Similar Prometheus alert relabelling
--alert.query-template="/graph?g0.expr={{.Expr}}&g0.tab=1"
Template to use in alerts source field.
Need only include {{.Expr}} parameter
--alert.query-url=ALERT.QUERY-URL
The external Thanos Query URL that would be set
in all alerts 'Source' field
Expand Down

0 comments on commit 63cd7a1

Please sign in to comment.