Пример #1
0
// Graph takes an OpenTSDB request data structure and queries OpenTSDB. Use the
// json parameter to pass JSON. Use the b64 parameter to pass base64-encoded
// JSON.
func Graph(t miniprofiler.Timer, w http.ResponseWriter, r *http.Request) (interface{}, error) {
	j := []byte(r.FormValue("json"))
	if bs := r.FormValue("b64"); bs != "" {
		b, err := base64.StdEncoding.DecodeString(bs)
		if err != nil {
			return nil, err
		}
		j = b
	}
	if len(j) == 0 {
		return nil, fmt.Errorf("either json or b64 required")
	}
	oreq, err := opentsdb.RequestFromJSON(j)
	if err != nil {
		return nil, err
	}
	if ads_v := r.FormValue("autods"); ads_v != "" {
		ads_i, err := strconv.Atoi(ads_v)
		if err != nil {
			return nil, err
		}
		if err := oreq.AutoDownsample(ads_i); err != nil {
			return nil, err
		}
	}
	ar := make(map[int]bool)
	for _, v := range r.Form["autorate"] {
		if i, err := strconv.Atoi(v); err == nil {
			ar[i] = true
		}
	}
	queries := make([]string, len(oreq.Queries))
	var start, end string
	var startT, endT time.Time
	if s, ok := oreq.Start.(string); ok && strings.Contains(s, "-ago") {
		startT, err = opentsdb.ParseTime(s)
		if err != nil {
			return nil, err
		}
		start = strings.TrimSuffix(s, "-ago")
	}
	if s, ok := oreq.End.(string); ok && strings.Contains(s, "-ago") {
		endT, err = opentsdb.ParseTime(s)
		if err != nil {
			return nil, err
		}
		end = strings.TrimSuffix(s, "-ago")
	}
	if start == "" && end == "" {
		s, sok := oreq.Start.(int64)
		e, eok := oreq.End.(int64)
		if sok && eok {
			start = fmt.Sprintf("%vs", e-s)
			startT = time.Unix(s, 0)
			endT = time.Unix(e, 0)
			if err != nil {
				return nil, err
			}
		}
	}
	if endT.Equal(time.Time{}) {
		endT = time.Now().UTC()
	}
	m_units := make(map[string]string)
	for i, q := range oreq.Queries {
		if ar[i] {

			meta, err := schedule.MetadataMetrics(q.Metric)
			if err != nil {
				return nil, err
			}
			if meta == nil {
				return nil, fmt.Errorf("no metadata for %s: cannot use auto rate", q)
			}
			if meta.Unit != "" {
				m_units[q.Metric] = meta.Unit
			}
			if meta.Rate != "" {
				switch meta.Rate {
				case metadata.Gauge:
					// ignore
				case metadata.Rate:
					q.Rate = true
				case metadata.Counter:
					q.Rate = true
					q.RateOptions = opentsdb.RateOptions{
						Counter:    true,
						ResetValue: 1,
					}
				default:
					return nil, fmt.Errorf("unknown metadata rate: %s", meta.Rate)
				}
			}
		}
		queries[i] = fmt.Sprintf(`q("%v", "%v", "%v")`, q, start, end)
		if !schedule.SystemConf.GetTSDBContext().Version().FilterSupport() {
			if err := schedule.Search.Expand(q); err != nil {
				return nil, err
			}
		}
	}
	var tr opentsdb.ResponseSet
	b, _ := json.MarshalIndent(oreq, "", "  ")
	t.StepCustomTiming("tsdb", "query", string(b), func() {
		h := schedule.SystemConf.GetTSDBHost()
		if h == "" {
			err = fmt.Errorf("tsdbHost not set")
			return
		}
		tr, err = oreq.Query(h)
	})
	if err != nil {
		return nil, err
	}
	cs, err := makeChart(tr, m_units)
	if err != nil {
		return nil, err
	}
	if _, present := r.Form["png"]; present {
		c := chart.ScatterChart{
			Title: fmt.Sprintf("%v - %v", oreq.Start, queries),
		}
		c.XRange.Time = true
		if min, err := strconv.ParseFloat(r.FormValue("min"), 64); err == nil {
			c.YRange.MinMode.Fixed = true
			c.YRange.MinMode.Value = min
		}
		if max, err := strconv.ParseFloat(r.FormValue("max"), 64); err == nil {
			c.YRange.MaxMode.Fixed = true
			c.YRange.MaxMode.Value = max
		}
		for ri, r := range cs {
			pts := make([]chart.EPoint, len(r.Data))
			for idx, v := range r.Data {
				pts[idx].X = v[0]
				pts[idx].Y = v[1]
			}
			slice.Sort(pts, func(i, j int) bool {
				return pts[i].X < pts[j].X
			})
			c.AddData(r.Name, pts, chart.PlotStyleLinesPoints, sched.Autostyle(ri))
		}
		w.Header().Set("Content-Type", "image/svg+xml")
		white := color.RGBA{0xff, 0xff, 0xff, 0xff}
		const width = 800
		const height = 600
		s := svg.New(w)
		s.Start(width, height)
		s.Rect(0, 0, width, height, "fill: #ffffff")
		sgr := svgg.AddTo(s, 0, 0, width, height, "", 12, white)
		c.Plot(sgr)
		s.End()
		return nil, nil
	}
	var a []annotate.Annotation
	warnings := []string{}
	if schedule.SystemConf.AnnotateEnabled() {
		a, err = annotateBackend.GetAnnotations(&startT, &endT)
		if err != nil {
			warnings = append(warnings, fmt.Sprintf("unable to get annotations: %v", err))
		}
	}
	return struct {
		Queries     []string
		Series      []*chartSeries
		Annotations []annotate.Annotation
		Warnings    []string
	}{
		queries,
		cs,
		a,
		warnings,
	}, nil
}
Пример #2
0
func main() {
	flag.Parse()
	if *tsdbHost == "" {
		flag.PrintDefaults()
		log.Fatal("host must be supplied")
	}
	putUrl := (&url.URL{Scheme: "http", Host: *tsdbHost, Path: "api/put"}).String()

	if *ruleFlag == "" {
		flag.PrintDefaults()
		log.Fatal("rule must be supplied")
	}
	rules, err := denormalize.ParseDenormalizationRules(*ruleFlag)
	if err != nil {
		log.Fatal(err)
	}
	if len(rules) > 1 {
		log.Fatal("Please specify only one rule")
	}
	var rule *denormalize.DenormalizationRule
	var metric string
	for k, v := range rules {
		metric = k
		rule = v
	}

	query := &opentsdb.Query{Metric: metric, Aggregator: "avg"}
	query.Tags, err = queryForAggregateTags(query)
	if err != nil {
		log.Fatal(err)
	}

	startDate, err := opentsdb.ParseTime(*start)
	if err != nil {
		log.Fatal(err)
	}
	endDate := time.Now().UTC()
	if *end != "" {
		endDate, err = opentsdb.ParseTime(*end)
		if err != nil {
			log.Fatal(err)
		}
	}

	backfill := func(batchStart, batchEnd time.Time) (err error) {
		startTimeString := batchStart.Format(opentsdb.TSDBTimeFormat)
		endTimeString := batchEnd.Format(opentsdb.TSDBTimeFormat)
		defer func() {
			if err != nil {
				log.Fatalf("Error on batch %s - %s. %v \n", startTimeString, endTimeString, err)
			}
		}()
		req := opentsdb.Request{Start: startTimeString, End: endTimeString, Queries: []*opentsdb.Query{query}}
		resp, err := req.Query(*tsdbHost)
		if err != nil {
			return err
		}
		dps := []*opentsdb.DataPoint{}
		for _, r := range resp {
			for t, p := range r.DPS {

				timeStamp, err := strconv.ParseInt(t, 10, 64)
				if err != nil {
					return err
				}
				dp := &opentsdb.DataPoint{
					Timestamp: timeStamp,
					Metric:    r.Metric,
					Tags:      r.Tags,
					Value:     p,
				}
				err = rule.Translate(dp)
				if err != nil {
					return err
				}
				dps = append(dps, dp)
			}
		}
		fmt.Printf("%s - %s: %d dps\n", startTimeString, endTimeString, len(dps))
		total := 0
		for len(dps) > 0 {
			count := len(dps)
			if len(dps) > *batchSize {
				count = *batchSize
			}
			putResp, err := collect.SendDataPoints(dps[:count], putUrl)
			if err != nil {
				return err
			}
			defer putResp.Body.Close()

			if putResp.StatusCode != 204 {
				return fmt.Errorf("Non 204 status code from opentsdb: %d", putResp.StatusCode)
			}
			dps = dps[count:]
			total += count
		}
		fmt.Printf("Relayed %d data points.\n", total)
		return nil
	}

	// walk backwards a day at a time
	curEnd := endDate
	for curEnd.After(startDate) {
		curStart := curEnd.Add(-24 * time.Hour)
		if curStart.Before(startDate) {
			curStart = startDate
		}
		backfill(curStart, curEnd)
		curEnd = curEnd.Add(-24 * time.Hour)
	}
}