// NewAggregate takes a named aggregating function `[float64] => float64` and makes it into a MetricFunction. func NewAggregate(name string, aggregator func([]float64) float64) function.MetricFunction { return function.MetricFunction{ Name: name, MinArguments: 1, MaxArguments: 1, AllowsGroupBy: true, Compute: func(context function.EvaluationContext, args []function.Expression, groups []string) (function.Value, error) { argument := args[0] value, err := argument.Evaluate(context) if err != nil { return nil, err } seriesList, err := value.ToSeriesList(context.Timerange) if err != nil { return nil, err } result := aggregate.AggregateBy(seriesList, aggregator, groups) groupNames := make([]string, len(groups)) for i, group := range groups { groupNames[i] += group } if len(groups) == 0 { result.Name = fmt.Sprintf("%s(%s)", name, value.GetName()) } else { result.Name = fmt.Sprintf("%s(%s group by %s)", name, value.GetName(), strings.Join(groupNames, ", ")) } return function.SeriesListValue(result), nil }, } }
// NewFilter creates a new instance of a filtering function. func NewFilter(name string, summary func([]float64) float64, ascending bool) function.MetricFunction { return function.MetricFunction{ Name: name, MinArguments: 2, MaxArguments: 2, Compute: func(context function.EvaluationContext, arguments []function.Expression, groups []string) (function.Value, error) { value, err := arguments[0].Evaluate(context) if err != nil { return nil, err } // The value must be a SeriesList. list, err := value.ToSeriesList(context.Timerange) if err != nil { return nil, err } countValue, err := arguments[1].Evaluate(context) if err != nil { return nil, err } countFloat, err := countValue.ToScalar() if err != nil { return nil, err } // Round to the nearest integer. count := int(countFloat + 0.5) if count < 0 { return nil, fmt.Errorf("expected positive count but got %d", count) } result := filter.FilterBy(list, count, summary, ascending) result.Name = fmt.Sprintf("%s(%s, %d)", name, value.GetName(), count) return function.SeriesListValue(result), nil }, } }
func (expr *metricFetchExpression) Evaluate(context function.EvaluationContext) (function.Value, error) { // Merge predicates appropriately var predicate api.Predicate if context.Predicate == nil && expr.predicate == nil { predicate = api.TruePredicate } else if context.Predicate == nil { predicate = expr.predicate } else if expr.predicate == nil { predicate = context.Predicate } else { predicate = &andPredicate{[]api.Predicate{expr.predicate, context.Predicate}} } metricTagSets, err := context.API.GetAllTags(api.MetricKey(expr.metricName)) if err != nil { return nil, err } filtered := applyPredicates(metricTagSets, predicate) ok := context.FetchLimit.Consume(len(filtered)) if !ok { return nil, function.NewLimitError("fetch limit exceeded: too many series to fetch", context.FetchLimit.Current(), context.FetchLimit.Limit()) } metrics := make([]api.TaggedMetric, len(filtered)) for i := range metrics { metrics[i] = api.TaggedMetric{api.MetricKey(expr.metricName), filtered[i]} } serieslist, err := context.MultiBackend.FetchMultipleSeries( api.FetchMultipleRequest{ metrics, context.SampleMethod, context.Timerange, context.API, context.Cancellable, context.Profiler, }, ) if err != nil { return nil, err } serieslist.Name = expr.metricName return function.SeriesListValue(serieslist), nil }
// NewOperator creates a new binary operator function. // the binary operators display a natural join semantic. func NewOperator(op string, operator func(float64, float64) float64) function.MetricFunction { return function.MetricFunction{ Name: op, MinArguments: 2, MaxArguments: 2, Compute: func(context function.EvaluationContext, args []function.Expression, groups function.Groups) (function.Value, error) { evaluated, err := function.EvaluateMany(context, args) if err != nil { return nil, err } leftValue := evaluated[0] rightValue := evaluated[1] leftList, err := leftValue.ToSeriesList(context.Timerange) if err != nil { return nil, err } rightList, err := rightValue.ToSeriesList(context.Timerange) if err != nil { return nil, err } joined := join.Join([]api.SeriesList{leftList, rightList}) result := make([]api.Timeseries, len(joined.Rows)) for i, row := range joined.Rows { left := row.Row[0] right := row.Row[1] array := make([]float64, len(left.Values)) for j := 0; j < len(left.Values); j++ { array[j] = operator(left.Values[j], right.Values[j]) } result[i] = api.Timeseries{array, row.TagSet} } return function.SeriesListValue(api.SeriesList{ Series: result, Timerange: context.Timerange, Name: fmt.Sprintf("(%s %s %s)", leftValue.GetName(), op, rightValue.GetName()), }), nil }, } }
// NewTransform takes a named transforming function `[float64], [value] => [float64]` and makes it into a MetricFunction. func NewTransform(name string, parameterCount int, transformer func([]float64, []function.Value, float64) ([]float64, error)) function.MetricFunction { return function.MetricFunction{ Name: name, MinArguments: parameterCount + 1, MaxArguments: parameterCount + 1, Compute: func(context function.EvaluationContext, args []function.Expression, groups []string) (function.Value, error) { listValue, err := args[0].Evaluate(context) if err != nil { return nil, err } list, err := listValue.ToSeriesList(context.Timerange) if err != nil { return nil, err } parameters := make([]function.Value, parameterCount) for i := range parameters { parameters[i], err = args[i+1].Evaluate(context) if err != nil { return nil, err } } result, err := transform.ApplyTransform(list, transformer, parameters) if err != nil { return nil, err } parameterNames := make([]string, len(parameters)) for i, param := range parameters { parameterNames[i] = param.GetName() } if len(parameters) != 0 { result.Name = fmt.Sprintf("%s(%s, %s)", name, listValue.GetName(), strings.Join(parameterNames, ", ")) } else { result.Name = fmt.Sprintf("%s(%s)", name, listValue.GetName()) } return function.SeriesListValue(result), nil }, } }
return nil, err } list, err := result.ToSeriesList(context.Timerange) if err != nil { return nil, err } value, err := arguments[1].Evaluate(context) if err != nil { return nil, err } dropTag, err := value.ToString() if err != nil { return nil, err } // Drop the tag from the list. return function.SeriesListValue(DropTag(list, dropTag)), nil }, } // SetFunction wraps up SetTag into a MetricFunction called "tag.set" var SetFunction = function.MetricFunction{ Name: "tag.set", MinArguments: 3, MaxArguments: 3, Compute: func(context function.EvaluationContext, arguments []function.Expression, groups function.Groups) (function.Value, error) { result, err := arguments[0].Evaluate(context) if err != nil { return nil, err } list, err := result.ToSeriesList(context.Timerange) if err != nil {
func (expr *LiteralSeriesExpression) Evaluate(context function.EvaluationContext) (function.Value, error) { return function.SeriesListValue(expr.list), nil }
func (expr *LiteralExpression) Evaluate(context function.EvaluationContext) (function.Value, error) { return function.SeriesListValue(api.SeriesList{ Series: []api.Timeseries{api.Timeseries{expr.Values, api.NewTagSet()}}, Timerange: api.Timerange{}, }), nil }
sum -= series.Values[i-limit] count-- } // Numerical error could (possibly) cause count == 0 but sum != 0. if i-limit+1 >= 0 { if count == 0 { results[i-limit+1] = math.NaN() } else { results[i-limit+1] = sum / float64(count) } } } list.Series[index].Values = results } list.Name = fmt.Sprintf("transform.moving_average(%s, %s)", listValue.GetName(), sizeValue.GetName()) return function.SeriesListValue(list), nil }, } var Alias = function.MetricFunction{ Name: "transform.alias", MinArguments: 2, MaxArguments: 2, Compute: func(context function.EvaluationContext, arguments []function.Expression, groups []string) (function.Value, error) { value, err := arguments[0].Evaluate(context) if err != nil { return nil, err } list, err := value.ToSeriesList(context.Timerange) if err != nil { return nil, err
// NewOperator creates a new binary operator function. // the binary operators display a natural join semantic. func NewOperator(op string, operator func(float64, float64) float64) function.MetricFunction { return function.MetricFunction{ Name: op, MinArguments: 2, MaxArguments: 2, Compute: func(context function.EvaluationContext, args []function.Expression, groups []string) (function.Value, error) { leftChannel := make(chan function.Value, 1) rightChannel := make(chan function.Value, 1) errs := make(chan error, 2) go func() { leftValue, err := args[0].Evaluate(context) leftChannel <- leftValue errs <- err }() go func() { rightValue, err := args[1].Evaluate(context) rightChannel <- rightValue errs <- err }() err := <-errs if err != nil { return nil, err } err = <-errs if err != nil { return nil, err } leftValue := <-leftChannel rightValue := <-rightChannel leftList, err := leftValue.ToSeriesList(context.Timerange) if err != nil { return nil, err } rightList, err := rightValue.ToSeriesList(context.Timerange) if err != nil { return nil, err } joined := join.Join([]api.SeriesList{leftList, rightList}) result := make([]api.Timeseries, len(joined.Rows)) for i, row := range joined.Rows { left := row.Row[0] right := row.Row[1] array := make([]float64, len(left.Values)) for j := 0; j < len(left.Values); j++ { array[j] = operator(left.Values[j], right.Values[j]) } result[i] = api.Timeseries{array, row.TagSet} } return function.SeriesListValue(api.SeriesList{ Series: result, Timerange: context.Timerange, Name: fmt.Sprintf("(%s %s %s)", leftValue.GetName(), op, rightValue.GetName()), }), nil }, } }