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 := &model.Sample{ Metric: model.Metric{}, Value: model.SampleValue(m.Counter.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 parseTime(s string) (clientmodel.Timestamp, error) { if t, err := strconv.ParseFloat(s, 64); err == nil { ts := int64(t * float64(time.Second)) return clientmodel.TimestampFromUnixNano(ts), nil } if t, err := time.Parse(time.RFC3339Nano, s); err == nil { return clientmodel.TimestampFromTime(t), nil } return 0, fmt.Errorf("cannot parse %q to a valid timestamp", s) }
func parseTimestampOrNow(t string, now clientmodel.Timestamp) (clientmodel.Timestamp, error) { if t == "" { return now, nil } tFloat, err := strconv.ParseFloat(t, 64) if err != nil { return 0, err } return clientmodel.TimestampFromUnixNano(int64(tFloat * float64(time.Second/time.Nanosecond))), nil }
// QueryRange handles the /api/query_range endpoint. func (serv MetricsService) QueryRange(w http.ResponseWriter, r *http.Request) { setAccessControlHeaders(w) w.Header().Set("Content-Type", "application/json") params := httputils.GetQueryParams(r) expr := params.Get("expr") // Input times and durations are in seconds and get converted to nanoseconds. endFloat, _ := strconv.ParseFloat(params.Get("end"), 64) durationFloat, _ := strconv.ParseFloat(params.Get("range"), 64) stepFloat, _ := strconv.ParseFloat(params.Get("step"), 64) nanosPerSecond := int64(time.Second / time.Nanosecond) end := int64(endFloat) * nanosPerSecond duration := int64(durationFloat) * nanosPerSecond step := int64(stepFloat) * nanosPerSecond exprNode, err := rules.LoadExprFromString(expr) if err != nil { fmt.Fprint(w, ast.ErrorToJSON(err)) return } if exprNode.Type() != ast.VectorType { fmt.Fprint(w, ast.ErrorToJSON(errors.New("expression does not evaluate to vector type"))) return } if end == 0 { end = clientmodel.Now().UnixNano() } if step <= 0 { step = nanosPerSecond } if end-duration < 0 { duration = end } // For safety, limit the number of returned points per timeseries. // This is sufficient for 60s resolution for a week or 1h resolution for a year. if duration/step > 11000 { fmt.Fprint(w, ast.ErrorToJSON(errors.New("exceeded maximum resolution of 11,000 points per timeseries. Try decreasing the query resolution (?step=XX)"))) return } // Align the start to step "tick" boundary. end -= end % step queryStats := stats.NewTimerGroup() evalTimer := queryStats.GetTimer(stats.TotalEvalTime).Start() matrix, err := ast.EvalVectorRange( exprNode.(ast.VectorNode), clientmodel.TimestampFromUnixNano(end-duration), clientmodel.TimestampFromUnixNano(end), time.Duration(step), serv.Storage, queryStats) if err != nil { fmt.Fprint(w, ast.ErrorToJSON(err)) return } evalTimer.Stop() sortTimer := queryStats.GetTimer(stats.ResultSortTime).Start() sort.Sort(matrix) sortTimer.Stop() jsonTimer := queryStats.GetTimer(stats.JSONEncodeTime).Start() result := ast.TypedValueToJSON(matrix, "matrix") jsonTimer.Stop() glog.V(1).Infof("Range query: %s\nQuery stats:\n%s\n", expr, queryStats) fmt.Fprint(w, result) }
// NewTemplateExpander returns a template expander ready to use. func NewTemplateExpander(text string, name string, data interface{}, timestamp clientmodel.Timestamp, queryEngine *promql.Engine, pathPrefix string) *templateExpander { return &templateExpander{ text: text, name: name, data: data, funcMap: text_template.FuncMap{ "query": func(q string) (queryResult, error) { return query(q, timestamp, queryEngine) }, "first": func(v queryResult) (*sample, error) { if len(v) > 0 { return v[0], nil } return nil, errors.New("first() called on vector with no elements") }, "label": func(label string, s *sample) string { return s.Labels[label] }, "value": func(s *sample) float64 { return s.Value }, "strvalue": func(s *sample) string { return s.Labels["__value__"] }, "args": func(args ...interface{}) map[string]interface{} { result := make(map[string]interface{}) for i, a := range args { result[fmt.Sprintf("arg%d", i)] = a } return result }, "reReplaceAll": func(pattern, repl, text string) string { re := regexp.MustCompile(pattern) return re.ReplaceAllString(text, repl) }, "safeHtml": func(text string) html_template.HTML { return html_template.HTML(text) }, "match": regexp.MatchString, "title": strings.Title, "graphLink": strutil.GraphLinkForExpression, "tableLink": strutil.TableLinkForExpression, "sortByLabel": func(label string, v queryResult) queryResult { sorter := queryResultByLabelSorter{v[:], label} sort.Stable(sorter) return v }, "humanize": func(v float64) string { if v == 0 || math.IsNaN(v) || math.IsInf(v, 0) { return fmt.Sprintf("%.4g", v) } if math.Abs(v) >= 1 { prefix := "" for _, p := range []string{"k", "M", "G", "T", "P", "E", "Z", "Y"} { if math.Abs(v) < 1000 { break } prefix = p v /= 1000 } return fmt.Sprintf("%.4g%s", v, prefix) } prefix := "" for _, p := range []string{"m", "u", "n", "p", "f", "a", "z", "y"} { if math.Abs(v) >= 1 { break } prefix = p v *= 1000 } return fmt.Sprintf("%.4g%s", v, prefix) }, "humanize1024": func(v float64) string { if math.Abs(v) <= 1 || math.IsNaN(v) || math.IsInf(v, 0) { return fmt.Sprintf("%.4g", v) } prefix := "" for _, p := range []string{"ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi", "Yi"} { if math.Abs(v) < 1024 { break } prefix = p v /= 1024 } return fmt.Sprintf("%.4g%s", v, prefix) }, "humanizeDuration": func(v float64) string { if math.IsNaN(v) || math.IsInf(v, 0) { return fmt.Sprintf("%.4g", v) } if v == 0 { return fmt.Sprintf("%.4gs", v) } if math.Abs(v) >= 1 { sign := "" if v < 0 { sign = "-" v = -v } seconds := int64(v) % 60 minutes := (int64(v) / 60) % 60 hours := (int64(v) / 60 / 60) % 24 days := (int64(v) / 60 / 60 / 24) // For days to minutes, we display seconds as an integer. if days != 0 { return fmt.Sprintf("%s%dd %dh %dm %ds", sign, days, hours, minutes, seconds) } if hours != 0 { return fmt.Sprintf("%s%dh %dm %ds", sign, hours, minutes, seconds) } if minutes != 0 { return fmt.Sprintf("%s%dm %ds", sign, minutes, seconds) } // For seconds, we display 4 significant digts. return fmt.Sprintf("%s%.4gs", sign, v) } prefix := "" for _, p := range []string{"m", "u", "n", "p", "f", "a", "z", "y"} { if math.Abs(v) >= 1 { break } prefix = p v *= 1000 } return fmt.Sprintf("%.4g%ss", v, prefix) }, "humanizeTimestamp": func(v float64) string { if math.IsNaN(v) || math.IsInf(v, 0) { return fmt.Sprintf("%.4g", v) } t := clientmodel.TimestampFromUnixNano(int64(v * 1e9)).Time().UTC() return fmt.Sprint(t) }, "pathPrefix": func() string { return pathPrefix }, }, } }
func extractHistogram(out Ingester, o *ProcessOptions, f *dto.MetricFamily) error { samples := make(model.Samples, 0, len(f.Metric)) for _, m := range f.Metric { if m.Histogram == nil { continue } timestamp := o.Timestamp if m.TimestampMs != nil { timestamp = model.TimestampFromUnixNano(*m.TimestampMs * 1000000) } infSeen := false for _, q := range m.Histogram.Bucket { sample := &model.Sample{ Metric: model.Metric{}, Value: model.SampleValue(q.GetCumulativeCount()), Timestamp: timestamp, } samples = append(samples, sample) metric := sample.Metric for _, p := range m.Label { metric[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) } metric[model.LabelName(model.BucketLabel)] = model.LabelValue(fmt.Sprint(q.GetUpperBound())) metric[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_bucket") if math.IsInf(q.GetUpperBound(), +1) { infSeen = true } } if m.Histogram.SampleSum != nil { sum := &model.Sample{ Metric: model.Metric{}, Value: model.SampleValue(m.Histogram.GetSampleSum()), Timestamp: timestamp, } samples = append(samples, sum) metric := sum.Metric for _, p := range m.Label { metric[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) } metric[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_sum") } if m.Histogram.SampleCount != nil { count := &model.Sample{ Metric: model.Metric{}, Value: model.SampleValue(m.Histogram.GetSampleCount()), Timestamp: timestamp, } samples = append(samples, count) metric := count.Metric for _, p := range m.Label { metric[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) } metric[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_count") if !infSeen { infBucket := &model.Sample{ Metric: model.Metric{}, Value: count.Value, Timestamp: timestamp, } samples = append(samples, infBucket) metric := infBucket.Metric for _, p := range m.Label { metric[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) } metric[model.LabelName(model.BucketLabel)] = model.LabelValue("+Inf") metric[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_bucket") } } } return out.Ingest(samples) }
func extractSummary(out Ingester, o *ProcessOptions, f *dto.MetricFamily) error { samples := make(model.Samples, 0, len(f.Metric)) for _, m := range f.Metric { if m.Summary == nil { continue } timestamp := o.Timestamp if m.TimestampMs != nil { timestamp = model.TimestampFromUnixNano(*m.TimestampMs * 1000000) } for _, q := range m.Summary.Quantile { sample := &model.Sample{ Metric: model.Metric{}, Value: model.SampleValue(q.GetValue()), Timestamp: timestamp, } samples = append(samples, sample) metric := sample.Metric for _, p := range m.Label { metric[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) } // BUG(matt): Update other names to "quantile". metric[model.LabelName(model.QuantileLabel)] = model.LabelValue(fmt.Sprint(q.GetQuantile())) metric[model.MetricNameLabel] = model.LabelValue(f.GetName()) } if m.Summary.SampleSum != nil { sum := &model.Sample{ Metric: model.Metric{}, Value: model.SampleValue(m.Summary.GetSampleSum()), Timestamp: timestamp, } samples = append(samples, sum) metric := sum.Metric for _, p := range m.Label { metric[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) } metric[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_sum") } if m.Summary.SampleCount != nil { count := &model.Sample{ Metric: model.Metric{}, Value: model.SampleValue(m.Summary.GetSampleCount()), Timestamp: timestamp, } samples = append(samples, count) metric := count.Metric for _, p := range m.Label { metric[model.LabelName(p.GetName())] = model.LabelValue(p.GetValue()) } metric[model.MetricNameLabel] = model.LabelValue(f.GetName() + "_count") } } return out.Ingest(samples) }