// === changes(matrix ExprMatrix) Vector === func funcChanges(ev *evaluator, args Expressions) Value { in := ev.evalMatrix(args[0]) out := make(Vector, 0, len(in)) for _, samples := range in { changes := 0 prev := clientmodel.SampleValue(samples.Values[0].Value) for _, sample := range samples.Values[1:] { current := sample.Value if current != prev { changes++ } prev = current } rs := &Sample{ Metric: samples.Metric, Value: clientmodel.SampleValue(changes), Timestamp: ev.Timestamp, } rs.Metric.Delete(clientmodel.MetricNameLabel) out = append(out, rs) } return out }
// scalarBinop evaluates a binary operation between two scalars. func scalarBinop(op itemType, lhs, rhs clientmodel.SampleValue) clientmodel.SampleValue { switch op { case itemADD: return lhs + rhs case itemSUB: return lhs - rhs case itemMUL: return lhs * rhs case itemDIV: return lhs / rhs case itemMOD: if rhs != 0 { return clientmodel.SampleValue(int(lhs) % int(rhs)) } return clientmodel.SampleValue(math.NaN()) case itemEQL: return btos(lhs == rhs) case itemNEQ: return btos(lhs != rhs) case itemGTR: return btos(lhs > rhs) case itemLSS: return btos(lhs < rhs) case itemGTE: return btos(lhs >= rhs) case itemLTE: return btos(lhs <= rhs) } panic(fmt.Errorf("operator %q not allowed for scalar operations", op)) }
// === predict_linear(node ExprMatrix, k ExprScalar) Vector === func funcPredictLinear(ev *evaluator, args Expressions) Value { vector := funcDeriv(ev, args[0:1]).(Vector) duration := clientmodel.SampleValue(clientmodel.SampleValue(ev.evalFloat(args[1]))) excludedLabels := map[clientmodel.LabelName]struct{}{ clientmodel.MetricNameLabel: {}, } // Calculate predicted delta over the duration. signatureToDelta := map[uint64]clientmodel.SampleValue{} for _, el := range vector { signature := clientmodel.SignatureWithoutLabels(el.Metric.Metric, excludedLabels) signatureToDelta[signature] = el.Value * duration } // add predicted delta to last value. matrixBounds := ev.evalMatrixBounds(args[0]) outVec := make(Vector, 0, len(signatureToDelta)) for _, samples := range matrixBounds { if len(samples.Values) < 2 { continue } signature := clientmodel.SignatureWithoutLabels(samples.Metric.Metric, excludedLabels) delta, ok := signatureToDelta[signature] if ok { samples.Metric.Delete(clientmodel.MetricNameLabel) outVec = append(outVec, &Sample{ Metric: samples.Metric, Value: delta + samples.Values[1].Value, Timestamp: ev.Timestamp, }) } } return outVec }
// === scalar(node VectorNode) Scalar === func scalarImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { v := args[0].(VectorNode).Eval(timestamp) if len(v) != 1 { return clientmodel.SampleValue(math.NaN()) } return clientmodel.SampleValue(v[0].Value) }
// vectorElemBinop evaluates a binary operation between two vector elements. func vectorElemBinop(op itemType, lhs, rhs clientmodel.SampleValue) (clientmodel.SampleValue, bool) { switch op { case itemADD: return lhs + rhs, true case itemSUB: return lhs - rhs, true case itemMUL: return lhs * rhs, true case itemDIV: return lhs / rhs, true case itemMOD: if rhs != 0 { return clientmodel.SampleValue(int(lhs) % int(rhs)), true } return clientmodel.SampleValue(math.NaN()), true case itemEQL: return lhs, lhs == rhs case itemNEQ: return lhs, lhs != rhs case itemGTR: return lhs, lhs > rhs case itemLSS: return lhs, lhs < rhs case itemGTE: return lhs, lhs >= rhs case itemLTE: return lhs, lhs <= rhs } panic(fmt.Errorf("operator %q not allowed for operations between vectors", op)) }
func (t *target) recordScrapeHealth(ingester extraction.Ingester, timestamp clientmodel.Timestamp, healthy bool, scrapeDuration time.Duration) { healthMetric := clientmodel.Metric{} durationMetric := clientmodel.Metric{} for label, value := range t.baseLabels { healthMetric[label] = value durationMetric[label] = value } healthMetric[clientmodel.MetricNameLabel] = clientmodel.LabelValue(scrapeHealthMetricName) durationMetric[clientmodel.MetricNameLabel] = clientmodel.LabelValue(scrapeDurationMetricName) healthMetric[InstanceLabel] = clientmodel.LabelValue(t.URL()) durationMetric[InstanceLabel] = clientmodel.LabelValue(t.URL()) healthValue := clientmodel.SampleValue(0) if healthy { healthValue = clientmodel.SampleValue(1) } healthSample := &clientmodel.Sample{ Metric: healthMetric, Timestamp: timestamp, Value: healthValue, } durationSample := &clientmodel.Sample{ Metric: durationMetric, Timestamp: timestamp, Value: clientmodel.SampleValue(float64(scrapeDuration) / float64(time.Second)), } ingester.Ingest(clientmodel.Samples{healthSample, durationSample}) }
// === scalar(node ExprVector) Scalar === func funcScalar(ev *evaluator, args Expressions) Value { v := ev.evalVector(args[0]) if len(v) != 1 { return &Scalar{clientmodel.SampleValue(math.NaN()), ev.Timestamp} } return &Scalar{clientmodel.SampleValue(v[0].Value), ev.Timestamp} }
// sampleValueAtIndex implements chunkIterator. func (it *deltaEncodedChunkIterator) sampleValueAtIndex(idx int) clientmodel.SampleValue { offset := deltaHeaderBytes + idx*int(it.tBytes+it.vBytes) + int(it.tBytes) if it.isInt { switch it.vBytes { case d0: return it.baseV case d1: return it.baseV + clientmodel.SampleValue(int8(it.c[offset])) case d2: return it.baseV + clientmodel.SampleValue(int16(binary.LittleEndian.Uint16(it.c[offset:]))) case d4: return it.baseV + clientmodel.SampleValue(int32(binary.LittleEndian.Uint32(it.c[offset:]))) // No d8 for ints. default: panic("Invalid number of bytes for integer delta") } } else { switch it.vBytes { case d4: return it.baseV + clientmodel.SampleValue(math.Float32frombits(binary.LittleEndian.Uint32(it.c[offset:]))) case d8: // Take absolute value for d8. return clientmodel.SampleValue(math.Float64frombits(binary.LittleEndian.Uint64(it.c[offset:]))) default: panic("Invalid number of bytes for floating point delta") } } }
func AppendRepeatingValuesTests(p metric.Persistence, t test.Tester) { m := clientmodel.Metric{ clientmodel.MetricNameLabel: "errors_total", "controller": "foo", "operation": "bar", } increments := 10 repetitions := 500 for i := 0; i < increments; i++ { for j := 0; j < repetitions; j++ { time := clientmodel.Timestamp(0).Add(time.Duration(i) * time.Hour).Add(time.Duration(j) * time.Second) testAppendSamples(p, &clientmodel.Sample{ Value: clientmodel.SampleValue(i), Timestamp: time, Metric: m, }, t) } } v, ok := p.(metric.View) if !ok { // It's purely a benchmark for a Persistence that is not viewable. return } matchers := labelMatchersFromLabelSet(clientmodel.LabelSet{ clientmodel.MetricNameLabel: "errors_total", "controller": "foo", "operation": "bar", }) for i := 0; i < increments; i++ { for j := 0; j < repetitions; j++ { fingerprints, err := p.GetFingerprintsForLabelMatchers(matchers) if err != nil { t.Fatal(err) } if len(fingerprints) != 1 { t.Fatalf("expected %d fingerprints, got %d", 1, len(fingerprints)) } time := clientmodel.Timestamp(0).Add(time.Duration(i) * time.Hour).Add(time.Duration(j) * time.Second) samples := v.GetValueAtTime(fingerprints[0], time) if len(samples) == 0 { t.Fatal("expected at least one sample.") } expected := clientmodel.SampleValue(i) for _, sample := range samples { if sample.Value != expected { t.Fatalf("expected %v value, got %v", expected, sample.Value) } } } } }
// === delta(matrix MatrixNode, isCounter=0 ScalarNode) Vector === func deltaImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { matrixNode := args[0].(MatrixNode) isCounter := len(args) >= 2 && args[1].(ScalarNode).Eval(timestamp) > 0 resultVector := Vector{} // If we treat these metrics as counters, we need to fetch all values // in the interval to find breaks in the timeseries' monotonicity. // I.e. if a counter resets, we want to ignore that reset. var matrixValue Matrix if isCounter { matrixValue = matrixNode.Eval(timestamp) } else { matrixValue = matrixNode.EvalBoundaries(timestamp) } for _, samples := range matrixValue { // No sense in trying to compute a delta without at least two points. Drop // this vector element. if len(samples.Values) < 2 { continue } counterCorrection := clientmodel.SampleValue(0) lastValue := clientmodel.SampleValue(0) for _, sample := range samples.Values { currentValue := sample.Value if isCounter && currentValue < lastValue { counterCorrection += lastValue - currentValue } lastValue = currentValue } resultValue := lastValue - samples.Values[0].Value + counterCorrection targetInterval := args[0].(*MatrixSelector).interval sampledInterval := samples.Values[len(samples.Values)-1].Timestamp.Sub(samples.Values[0].Timestamp) if sampledInterval == 0 { // Only found one sample. Cannot compute a rate from this. continue } // Correct for differences in target vs. actual delta interval. // // Above, we didn't actually calculate the delta for the specified target // interval, but for an interval between the first and last found samples // under the target interval, which will usually have less time between // them. Depending on how many samples are found under a target interval, // the delta results are distorted and temporal aliasing occurs (ugly // bumps). This effect is corrected for below. intervalCorrection := clientmodel.SampleValue(targetInterval) / clientmodel.SampleValue(sampledInterval) resultValue *= intervalCorrection resultSample := &Sample{ Metric: samples.Metric, Value: resultValue, Timestamp: timestamp, } resultSample.Metric.Delete(clientmodel.MetricNameLabel) resultVector = append(resultVector, resultSample) } return resultVector }
func evalVectorBinop(opType BinOpType, lhs clientmodel.SampleValue, rhs clientmodel.SampleValue) (clientmodel.SampleValue, bool) { switch opType { case ADD: return lhs + rhs, true case SUB: return lhs - rhs, true case MUL: return lhs * rhs, true case DIV: if rhs != 0 { return lhs / rhs, true } return clientmodel.SampleValue(math.Inf(int(rhs))), true case MOD: if rhs != 0 { return clientmodel.SampleValue(int(lhs) % int(rhs)), true } return clientmodel.SampleValue(math.Inf(int(rhs))), true case EQ: if lhs == rhs { return lhs, true } return 0, false case NE: if lhs != rhs { return lhs, true } return 0, false case GT: if lhs > rhs { return lhs, true } return 0, false case LT: if lhs < rhs { return lhs, true } return 0, false case GE: if lhs >= rhs { return lhs, true } return 0, false case LE: if lhs <= rhs { return lhs, true } return 0, false case AND: return lhs, true case OR: return lhs, true // TODO: implement OR } panic("Not all enum values enumerated in switch") }
// interpolateSamples interpolates a value at a target time between two // provided sample pairs. func interpolateSamples(first, second *metric.SamplePair, timestamp clientmodel.Timestamp) *metric.SamplePair { dv := second.Value - first.Value dt := second.Timestamp.Sub(first.Timestamp) dDt := dv / clientmodel.SampleValue(dt) offset := clientmodel.SampleValue(timestamp.Sub(first.Timestamp)) return &metric.SamplePair{ Value: first.Value + (offset * dDt), Timestamp: timestamp, } }
func evalScalarBinop(opType BinOpType, lhs clientmodel.SampleValue, rhs clientmodel.SampleValue) clientmodel.SampleValue { switch opType { case ADD: return lhs + rhs case SUB: return lhs - rhs case MUL: return lhs * rhs case DIV: if rhs != 0 { return lhs / rhs } return clientmodel.SampleValue(math.Inf(int(rhs))) case MOD: if rhs != 0 { return clientmodel.SampleValue(int(lhs) % int(rhs)) } return clientmodel.SampleValue(math.Inf(int(rhs))) case EQ: if lhs == rhs { return 1 } return 0 case NE: if lhs != rhs { return 1 } return 0 case GT: if lhs > rhs { return 1 } return 0 case LT: if lhs < rhs { return 1 } return 0 case GE: if lhs >= rhs { return 1 } return 0 case LE: if lhs <= rhs { return 1 } return 0 } panic("Not all enum values enumerated in switch") }
func (c *deltaEncodedChunk) valueAtIndex(idx int) *metric.SamplePair { offset := deltaHeaderBytes + idx*c.sampleSize() var ts clientmodel.Timestamp switch c.timeBytes() { case d1: ts = c.baseTime() + clientmodel.Timestamp(uint8(c.buf[offset])) case d2: ts = c.baseTime() + clientmodel.Timestamp(binary.LittleEndian.Uint16(c.buf[offset:])) case d4: ts = c.baseTime() + clientmodel.Timestamp(binary.LittleEndian.Uint32(c.buf[offset:])) case d8: // Take absolute value for d8. ts = clientmodel.Timestamp(binary.LittleEndian.Uint64(c.buf[offset:])) default: panic("Invalid number of bytes for time delta") } offset += int(c.timeBytes()) var v clientmodel.SampleValue if c.isInt() { switch c.valueBytes() { case d0: v = c.baseValue() case d1: v = c.baseValue() + clientmodel.SampleValue(int8(c.buf[offset])) case d2: v = c.baseValue() + clientmodel.SampleValue(int16(binary.LittleEndian.Uint16(c.buf[offset:]))) case d4: v = c.baseValue() + clientmodel.SampleValue(int32(binary.LittleEndian.Uint32(c.buf[offset:]))) // No d8 for ints. default: panic("Invalid number of bytes for integer delta") } } else { switch c.valueBytes() { case d4: v = c.baseValue() + clientmodel.SampleValue(math.Float32frombits(binary.LittleEndian.Uint32(c.buf[offset:]))) case d8: // Take absolute value for d8. v = clientmodel.SampleValue(math.Float64frombits(binary.LittleEndian.Uint64(c.buf[offset:]))) default: panic("Invalid number of bytes for floating point delta") } } return &metric.SamplePair{ Timestamp: ts, Value: v, } }
// sampleValueAtIndex implements chunkIterator. func (it *doubleDeltaEncodedChunkIterator) sampleValueAtIndex(idx int) clientmodel.SampleValue { if idx == 0 { return it.baseV } if idx == 1 { // If value bytes are at d8, the value is saved directly rather // than as a difference. if it.vBytes == d8 { return it.baseΔV } return it.baseV + it.baseΔV } offset := doubleDeltaHeaderBytes + (idx-2)*int(it.tBytes+it.vBytes) + int(it.tBytes) if it.isInt { switch it.vBytes { case d0: return it.baseV + clientmodel.SampleValue(idx)*it.baseΔV case d1: return it.baseV + clientmodel.SampleValue(idx)*it.baseΔV + clientmodel.SampleValue(int8(it.c[offset])) case d2: return it.baseV + clientmodel.SampleValue(idx)*it.baseΔV + clientmodel.SampleValue(int16(binary.LittleEndian.Uint16(it.c[offset:]))) case d4: return it.baseV + clientmodel.SampleValue(idx)*it.baseΔV + clientmodel.SampleValue(int32(binary.LittleEndian.Uint32(it.c[offset:]))) // No d8 for ints. default: panic("Invalid number of bytes for integer delta") } } else { switch it.vBytes { case d4: return it.baseV + clientmodel.SampleValue(idx)*it.baseΔV + clientmodel.SampleValue(math.Float32frombits(binary.LittleEndian.Uint32(it.c[offset:]))) case d8: // Take absolute value for d8. return clientmodel.SampleValue(math.Float64frombits(binary.LittleEndian.Uint64(it.c[offset:]))) default: panic("Invalid number of bytes for floating point delta") } } }
func (s *testNotificationScenario) test(i int, t *testing.T) { h := NewNotificationHandler(&NotificationHandlerOptions{ AlertmanagerURL: "alertmanager_url", QueueCapacity: 0, Deadline: 10 * time.Second, }) defer h.Stop() receivedPost := make(chan bool, 1) poster := testHTTPPoster{receivedPost: receivedPost} h.httpClient = &poster go h.Run() h.SubmitReqs(NotificationReqs{ { Summary: s.summary, Description: s.description, Labels: clientmodel.LabelSet{ clientmodel.LabelName("instance"): clientmodel.LabelValue("testinstance"), }, Value: clientmodel.SampleValue(1.0 / 3.0), ActiveSince: time.Time{}, RuleString: "Test rule string", GeneratorURL: "prometheus_url", }, }) <-receivedPost if poster.message != s.message { t.Fatalf("%d. Expected '%s', received '%s'", i, s.message, poster.message) } }
func extractCounter(out Ingester, o *ProcessOptions, f *dto.MetricFamily) error { samples := make(model.Samples, 0, len(f.Metric)) for _, m := range f.Metric { if m.Counter == nil { continue } sample := new(model.Sample) samples = append(samples, sample) if m.TimestampMs != nil { sample.Timestamp = model.TimestampFromUnix(*m.TimestampMs / 1000) } else { sample.Timestamp = o.Timestamp } sample.Metric = model.Metric{} metric := sample.Metric for _, p := range m.Label { metric[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) } metric[model.MetricNameLabel] = model.LabelValue(f.GetName()) sample.Value = model.SampleValue(m.Counter.GetValue()) } return out.Ingest(&Result{Samples: samples}) }
func extractUntyped(out Ingester, o *ProcessOptions, f *dto.MetricFamily) error { samples := make(model.Samples, 0, len(f.Metric)) for _, m := range f.Metric { if m.Untyped == nil { continue } sample := &model.Sample{ Metric: model.Metric{}, Value: model.SampleValue(m.Untyped.GetValue()), } samples = append(samples, sample) if m.TimestampMs != nil { sample.Timestamp = model.TimestampFromUnixNano(*m.TimestampMs * 1000000) } else { sample.Timestamp = o.Timestamp } metric := sample.Metric for _, p := range m.Label { metric[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) } metric[model.MetricNameLabel] = model.LabelValue(f.GetName()) } return out.Ingest(samples) }
func AppendSampleAsPureSparseAppendTests(p metric.Persistence, t test.Tester) { appendSample := func(x int) (success bool) { v := clientmodel.SampleValue(x) ts := clientmodel.TimestampFromUnix(int64(x)) labelName := clientmodel.LabelName(x) labelValue := clientmodel.LabelValue(x) l := clientmodel.Metric{labelName: labelValue} sample := &clientmodel.Sample{ Value: v, Timestamp: ts, Metric: l, } err := p.AppendSamples(clientmodel.Samples{sample}) success = err == nil if !success { t.Error(err) } return } if err := quick.Check(appendSample, nil); err != nil { t.Error(err) } }
func neededDeltaBytes(deltaT clientmodel.Timestamp, deltaV clientmodel.SampleValue, isInt bool) (dtb, dvb deltaBytes) { dtb = d1 if deltaT > math.MaxUint8 { dtb = d2 } if deltaT > math.MaxUint16 { dtb = d4 } if deltaT > math.MaxUint32 { dtb = d8 } if isInt { dvb = d0 if deltaV != 0 { dvb = d1 } if deltaV < math.MinInt8 || deltaV > math.MaxInt8 { dvb = d2 } if deltaV < math.MinInt16 || deltaV > math.MaxInt16 { dvb = d4 } if deltaV < math.MinInt32 || deltaV > math.MaxInt32 { dvb = d8 } } else { dvb = d4 if clientmodel.SampleValue(float32(deltaV)) != deltaV { dvb = d8 } } return dtb, dvb }
func (t *Test) parseEval(lines []string, i int) (int, *evalCmd, error) { if !patEvalInstant.MatchString(lines[i]) { return i, nil, raise(i, "invalid evaluation command. (eval[_fail|_ordered] instant [at <offset:duration>] <query>") } parts := patEvalInstant.FindStringSubmatch(lines[i]) var ( mod = parts[1] at = parts[2] qry = parts[3] ) expr, err := ParseExpr(qry) if err != nil { if perr, ok := err.(*ParseErr); ok { perr.Line = i + 1 perr.Pos += strings.Index(lines[i], qry) } return i, nil, err } offset, err := strutil.StringToDuration(at) if err != nil { return i, nil, raise(i, "invalid step definition %q: %s", parts[1], err) } ts := testStartTime.Add(offset) cmd := newEvalCmd(expr, ts, ts, 0) switch mod { case "ordered": cmd.ordered = true case "fail": cmd.fail = true } for j := 1; i+1 < len(lines); j++ { i++ defLine := lines[i] if len(defLine) == 0 { i-- break } if f, err := parseNumber(defLine); err == nil { cmd.expect(0, nil, sequenceValue{value: clientmodel.SampleValue(f)}) break } metric, vals, err := parseSeriesDesc(defLine) if err != nil { if perr, ok := err.(*ParseErr); ok { perr.Line = i + 1 } return i, nil, err } // Currently, we are not expecting any matrices. if len(vals) > 1 { return i, nil, raise(i, "expecting multiple values in instant evaluation not allowed") } cmd.expect(j, metric, vals...) } return i, cmd, nil }
func (s *testNotificationScenario) test(i int, t *testing.T) { notifications := make(chan NotificationReqs) defer close(notifications) h := NewNotificationHandler("alertmanager_url", notifications) receivedPost := make(chan bool, 1) poster := testHttpPoster{receivedPost: receivedPost} h.httpClient = &poster go h.Run() notifications <- NotificationReqs{ { Summary: s.summary, Description: s.description, Labels: clientmodel.LabelSet{ clientmodel.LabelName("instance"): clientmodel.LabelValue("testinstance"), }, Value: clientmodel.SampleValue(1.0 / 3.0), ActiveSince: time.Time{}, RuleString: "Test rule string", GeneratorUrl: "prometheus_url", }, } <-receivedPost if poster.message != s.message { t.Fatalf("%d. Expected '%s', received '%s'", i, s.message, poster.message) } }
// TestLoop is just a smoke test for the loop method, if we can switch it on and // off without disaster. func TestLoop(t *testing.T) { samples := make(clientmodel.Samples, 1000) for i := range samples { samples[i] = &clientmodel.Sample{ Timestamp: clientmodel.Timestamp(2 * i), Value: clientmodel.SampleValue(float64(i) * 0.2), } } directory := test.NewTemporaryDirectory("test_storage", t) defer directory.Close() o := &MemorySeriesStorageOptions{ MemoryChunks: 50, PersistenceRetentionPeriod: 24 * 7 * time.Hour, PersistenceStoragePath: directory.Path(), CheckpointInterval: 250 * time.Millisecond, } storage, err := NewMemorySeriesStorage(o) if err != nil { t.Fatalf("Error creating storage: %s", err) } storage.Start() storage.AppendSamples(samples) time.Sleep(time.Second) storage.Stop() }
func TestSampleDelivery(t *testing.T) { // Let's create an even number of send batches so we don't run into the // batch timeout case. n := maxSamplesPerSend * 2 samples := make(clientmodel.Samples, 0, n) for i := 0; i < n; i++ { samples = append(samples, &clientmodel.Sample{ Metric: clientmodel.Metric{ clientmodel.MetricNameLabel: "test_metric", }, Value: clientmodel.SampleValue(i), }) } c := &TestTSDBClient{} c.expectSamples(samples[:len(samples)/2]) m := NewTSDBQueueManager(c, 1) // These should be received by the client. m.Queue(samples[:len(samples)/2]) // These will be dropped because the queue is full. m.Queue(samples[len(samples)/2:]) go m.Run() defer m.Stop() c.waitForExpectedSamples(t) }
// primaryExpr parses a primary expression. // // <metric_name> | <function_call> | <vector_aggregation> | <literal> // func (p *parser) primaryExpr() Expr { switch t := p.next(); { case t.typ == itemNumber: f := p.number(t.val) return &NumberLiteral{clientmodel.SampleValue(f)} case t.typ == itemString: s := t.val[1 : len(t.val)-1] return &StringLiteral{s} case t.typ == itemLeftBrace: // Metric selector without metric name. p.backup() return p.vectorSelector("") case t.typ == itemIdentifier: // Check for function call. if p.peek().typ == itemLeftParen { return p.call(t.val) } fallthrough // Else metric selector. case t.typ == itemMetricIdentifier: return p.vectorSelector(t.val) case t.typ.isAggregator(): p.backup() return p.aggrExpr() } return nil }
// === log10(vector ExprVector) Vector === func funcLog10(ev *evaluator, args Expressions) Value { vector := ev.evalVector(args[0]) for _, el := range vector { el.Metric.Delete(clientmodel.MetricNameLabel) el.Value = clientmodel.SampleValue(math.Log10(float64(el.Value))) } return vector }
// === avg_over_time(matrix MatrixNode) Vector === func avgOverTimeImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { return aggrOverTime(timestamp, args, func(values metric.Values) clientmodel.SampleValue { var sum clientmodel.SampleValue for _, v := range values { sum += v.Value } return sum / clientmodel.SampleValue(len(values)) }) }
// === max_over_time(matrix MatrixNode) Vector === func maxOverTimeImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { return aggrOverTime(timestamp, args, func(values metric.Values) clientmodel.SampleValue { max := math.Inf(-1) for _, v := range values { max = math.Max(max, float64(v.Value)) } return clientmodel.SampleValue(max) }) }
// === min_over_time(matrix MatrixNode) Vector === func minOverTimeImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { return aggrOverTime(timestamp, args, func(values metric.Values) clientmodel.SampleValue { min := math.Inf(1) for _, v := range values { min = math.Min(min, float64(v.Value)) } return clientmodel.SampleValue(min) }) }
// === abs(vector VectorNode) Vector === func absImpl(timestamp clientmodel.Timestamp, args []Node) interface{} { n := args[0].(VectorNode) vector := n.Eval(timestamp) for _, el := range vector { el.Metric.Delete(clientmodel.MetricNameLabel) el.Value = clientmodel.SampleValue(math.Abs(float64(el.Value))) } return vector }