Ejemplo n.º 1
0
func appMetricCsv(r *http.Request, h http.Header, b *bytes.Buffer) *weft.Result {
	a := appMetric{}

	v := r.URL.Query()
	applicationID := v.Get("applicationID")

	resolution := v.Get("resolution")
	if resolution == "" {
		resolution = "minute"
	}

	var timeRange []time.Time
	var err error
	if timeRange, err = parseTimeRange(v); err != nil {
		return weft.InternalServerError(err)
	}

	// the Plot type holds all the data used to plot svgs, we'll create a CSV from the labels and point values
	var p ts.Plot

	switch v.Get("group") {
	case "counters":
		if res := a.loadCounters(applicationID, resolution, timeRange, &p); !res.Ok {
			return res
		}
	case "timers":
		// "full" resolution for timers is 90th percentile max per minute over fourty days
		sourceID := v.Get("sourceID")
		if sourceID != "" {
			if res := a.loadTimersWithSourceID(applicationID, sourceID, resolution, timeRange, &p); !res.Ok {
				return res
			}
		} else {
			if res := a.loadTimers(applicationID, resolution, timeRange, &p); !res.Ok {
				return res
			}
		}
	case "memory":
		if res := a.loadMemory(applicationID, resolution, timeRange, &p); !res.Ok {
			return res
		}
	case "objects":
		if res := a.loadAppMetrics(applicationID, resolution, internal.MemHeapObjects, timeRange, &p); !res.Ok {
			return res
		}
	case "routines":
		if res := a.loadAppMetrics(applicationID, resolution, internal.Routines, timeRange, &p); !res.Ok {
			return res
		}
	default:
		return weft.BadRequest("invalid value for group")
	}

	// CSV headers, the first label is always time
	labels := p.GetLabels()
	var headers []string
	for _, label := range labels {
		headers = append(headers, label.Label)
	}

	// Labels can be in random order so keep a sorted list but with time always at 0
	sort.Strings(headers)
	headers = append([]string{"time"}, headers...)

	values := make(map[time.Time]map[string]float64)
	ts := times{} // maintaining an ordered and unique list of times in the map

	// add all points to a map to collect duplicate times with different column names
	allData := p.GetSeries()
	for i, d := range allData {
		points := d.Series.Points
		for _, point := range points {
			if _, ok := values[point.DateTime]; ok == false {
				values[point.DateTime] = map[string]float64{labels[i].Label: point.Value}
				ts = append(ts, point.DateTime)
			} else {
				v := values[point.DateTime]
				v[labels[i].Label] = point.Value
			}
		}
	}

	if len(values) == 0 {
		return &weft.StatusOK
	}

	w := csv.NewWriter(b)
	sort.Sort(ts)
	for i, t := range ts {

		// CSV headers
		if i == 0 {
			if err = w.Write(headers); err != nil {
				return weft.InternalServerError(err)
			}
		}

		fields := []string{t.Format(DYGRAPH_TIME_FORMAT)}

		// CSV data
		// start at index 1: because we've already written out the time
		for _, colName := range headers[1:] {

			val, ok := values[t][colName]
			if ok {
				fields = append(fields, fmt.Sprintf("%.2f", val))
			} else {
				// Dygraphs expected an empty CSV field for missing data.
				fields = append(fields, "")
			}
		}

		if err = w.Write(fields); err != nil {
			return weft.InternalServerError(err)
		}
	}

	w.Flush()
	if err := w.Error(); err != nil {
		return weft.InternalServerError(err)
	}

	return &weft.StatusOK
}