func (c *Conf) alert(s *expr.State, T miniprofiler.Timer, name, key string) (results *expr.Results, err error) { _, e, err := c.getAlertExpr(name, key) if err != nil { return nil, err } results, _, err = e.ExecuteState(s, T) if err != nil { return nil, err } if s.History != nil { unknownTags, unevalTags := s.History.GetUnknownAndUnevaluatedAlertKeys(name) // For currently unknown tags NOT in the result set, add an error result for _, ak := range unknownTags { found := false for _, result := range results.Results { if result.Group.Equal(ak.Group()) { found = true break } } if !found { res := expr.Result{ Value: expr.Number(1), Group: ak.Group(), } results.Results = append(results.Results, &res) } } //For all unevaluated tags in run history, make sure we report a nonzero result. for _, ak := range unevalTags { found := false for _, result := range results.Results { if result.Group.Equal(ak.Group()) { result.Value = expr.Number(1) found = true break } } if !found { res := expr.Result{ Value: expr.Number(1), Group: ak.Group(), } results.Results = append(results.Results, &res) } } } return results, nil }
// LeftJoin takes slices of results and expressions for which it gets the slices of results. // Then it joins the 2nd and higher slice of results onto the first slice of results. // Joining is performed by group: a group that includes all tags (with same values) of the first group is a match. func (c *Context) LeftJoin(v ...interface{}) (interface{}, error) { if len(v) < 2 { return nil, fmt.Errorf("need at least two values (each can be an expression or result slice), got %v", len(v)) } // temporarily store the results in a results[M][Ni] Result matrix: // for M queries, tracks Ni results per each i'th query results := make([][]*expr.Result, len(v)) for col, val := range v { queryResults, _, err := c.eval(val, false, false, 0) if err != nil { return nil, err } results[col] = queryResults } // perform the joining by storing all results in a joined[N0][M] Result matrix: // for N tagsets (based on first query results), tracks all M Results (results with matching group, from all other queries) joined := make([][]*expr.Result, 0) for row, firstQueryResult := range results[0] { joined = append(joined, make([]*expr.Result, len(v))) joined[row][0] = firstQueryResult // join results of 2nd to M queries for col, queryResults := range results[1:] { for _, laterQueryResult := range queryResults { if firstQueryResult.Group.Subset(laterQueryResult.Group) { joined[row][col+1] = laterQueryResult break } // Fill emtpy cells with NaN Value, so calling .Value is not a nil pointer dereference joined[row][col+1] = &expr.Result{Value: expr.Number(math.NaN())} } } } return joined, nil }
func init() { gob.Register(expr.Number(0)) gob.Register(expr.Scalar(0)) }
func (c *Conf) Funcs() map[string]eparse.Func { lookup := func(e *expr.State, T miniprofiler.Timer, lookup, key string) (results *expr.Results, err error) { results = new(expr.Results) results.IgnoreUnjoined = true l := c.Lookups[lookup] if l == nil { return nil, fmt.Errorf("lookup table not found: %v", lookup) } lookups := l.ToExpr() if lookups == nil { err = fmt.Errorf("lookup table not found: %v", lookup) return } var tags []opentsdb.TagSet for _, tag := range lookups.Tags { var next []opentsdb.TagSet vals, err := e.Search.TagValuesByTagKey(tag, 0) if err != nil { return nil, err } for _, value := range vals { for _, s := range tags { t := s.Copy() t[tag] = value next = append(next, t) } if len(tags) == 0 { next = append(next, opentsdb.TagSet{tag: value}) } } tags = next } for _, tag := range tags { value, ok := lookups.Get(key, tag) if !ok { continue } var num float64 num, err = strconv.ParseFloat(value, 64) if err != nil { return nil, err } results.Results = append(results.Results, &expr.Result{ Value: expr.Number(num), Group: tag, }) } return results, nil } lookupSeries := func(e *expr.State, T miniprofiler.Timer, series *expr.Results, lookup, key string) (results *expr.Results, err error) { results = new(expr.Results) results.IgnoreUnjoined = true l := c.Lookups[lookup] if l == nil { return nil, fmt.Errorf("lookup table not found: %v", lookup) } lookups := l.ToExpr() if lookups == nil { err = fmt.Errorf("lookup table not found: %v", lookup) return } for _, res := range series.Results { value, ok := lookups.Get(key, res.Group) if !ok { continue } var num float64 num, err = strconv.ParseFloat(value, 64) if err != nil { return nil, err } results.Results = append(results.Results, &expr.Result{ Value: expr.Number(num), Group: res.Group, }) } return results, nil } lookupTags := func(args []eparse.Node) (eparse.Tags, error) { name := args[0].(*eparse.StringNode).Text lookup := c.Lookups[name] if lookup == nil { return nil, fmt.Errorf("bad lookup table %v", name) } t := make(eparse.Tags) for _, v := range lookup.Tags { t[v] = struct{}{} } return t, nil } lookupSeriesTags := func(args []eparse.Node) (eparse.Tags, error) { name := args[1].(*eparse.StringNode).Text lookup := c.Lookups[name] if lookup == nil { return nil, fmt.Errorf("bad lookup table %v", name) } t := make(eparse.Tags) for _, v := range lookup.Tags { t[v] = struct{}{} } return t, nil } tagAlert := func(args []eparse.Node) (eparse.Tags, error) { name := args[0].(*eparse.StringNode).Text key := args[1].(*eparse.StringNode).Text a, e, err := c.getAlertExpr(name, key) if err != nil { return nil, err } if a.returnType != eparse.TypeNumberSet { return nil, fmt.Errorf("alert requires a number-returning expression (got %v)", a.returnType) } return e.Root.Tags() } funcs := map[string]eparse.Func{ "alert": { Args: []eparse.FuncType{eparse.TypeString, eparse.TypeString}, Return: eparse.TypeNumberSet, Tags: tagAlert, F: c.alert, }, "lookup": { Args: []eparse.FuncType{eparse.TypeString, eparse.TypeString}, Return: eparse.TypeNumberSet, Tags: lookupTags, F: lookup, }, "lookupSeries": { Args: []eparse.FuncType{eparse.TypeSeriesSet, eparse.TypeString, eparse.TypeString}, Return: eparse.TypeNumberSet, Tags: lookupSeriesTags, F: lookupSeries, }, } merge := func(fs map[string]eparse.Func) { for k, v := range fs { funcs[k] = v } } if c.TSDBHost != "" { merge(expr.TSDB) } if c.GraphiteHost != "" { merge(expr.Graphite) } if len(c.LogstashElasticHosts) != 0 { merge(expr.LogstashElastic) } if c.InfluxConfig.URL.Host != "" { merge(expr.Influx) } return funcs }