Example #1
0
func openTSDB(config ConfigApp, buffer *bytes.Buffer) error {
	logCtx := log.WithFields(log.Fields{
		"in":  "openTSDB",
		"ctx": "Metric format error",
	})

	maxMetrics := 10
	if i, err := strconv.Atoi(os.Getenv("STATSDAEMON_MAXMETRICS")); err == nil && i > 0 {
		maxMetrics = i
	}

	if config.OpenTSDBAddress != "-" && config.OpenTSDBAddress != "" {
		datapoints := []tsdb.DataPoint{}
		TSDB := tsdb.TSDB{}
		server := tsdb.Server{}
		serverAdress := strings.Split(config.OpenTSDBAddress, ":")
		if len(serverAdress) != 2 {
			return fmt.Errorf("Incorrect openTSDB server address %v", serverAdress)
		}
		port, err := strconv.ParseUint(serverAdress[1], 10, 32)
		if err != nil {
			return err
		}
		server.Host = serverAdress[0]
		server.Port = uint(port)
		TSDB.Servers = append(TSDB.Servers, server)

		metrics := strings.Split(buffer.String(), "\n")
		metrics = removeEmptyLines(metrics)
		num := len(metrics)
		currentMetricsNum := 0
		for idx, mtr := range metrics {
			// target format: cpu.load 12.50 112345566 host=dev,zone=west
			data := strings.Split(mtr, " ")
			if len(data) >= 3 {
				metric := tsdb.Metric{}
				value := tsdb.Value{}
				tags := tsdb.Tags{}
				timestamp := tsdb.Time{}
				datapoint := tsdb.DataPoint{}

				// parse value
				// K/V values NOT allowed for OpenTSDB as metric
				// TODO - consider setting them as tags
				val, err := strconv.ParseFloat(data[1], 64)
				if err != nil {
					// continue on error in one metric
					logCtx.WithField("after", "ParseFloat").Errorf("Only float/integer values allowed. Got: %s in line \"%s\". Error: %s", data[1], data, err)
					Stat.ErrorIncr()
					continue
				}
				value.Set(val)

				// parse timestamp
				err = timestamp.Parse(data[2])
				if err != nil {
					logCtx.WithField("after", "Parse").Errorf("Timestamp expected. Got: %s in line \"%s\". Error: %s", data[2], data, err)
					Stat.ErrorIncr()
					continue
				}

				// parse metric/bucket
				metricName := data[0]
				err = metric.Set(metricName)
				if err != nil {
					logCtx.WithField("after", "Set").Errorf("Metric name expected. Got: %s in line \"%s\". Error: %s", data[0], data, err)
					Stat.ErrorIncr()
					continue
				}

				if len(data) == 4 {
					combinedTagsSlice := strings.Split(data[3], ",")
					if len(combinedTagsSlice) > 0 {
						for _, e := range combinedTagsSlice {
							strSlice := strings.Split(e, "=")
							if len(strSlice) == 2 {
								if strSlice[0] == "" || strSlice[1] == "" {
									logCtx.WithField("after", "Split").Errorf("Tag  expected. Got: %s in line \"%s\"", e, data)
									Stat.ErrorIncr()
									continue
								}
								tags.Set(strSlice[0], strSlice[1])
							}
						}
					}
				}

				datapoint.Value = &value
				datapoint.Metric = &metric
				datapoint.Tags = &tags
				datapoint.Timestamp = &timestamp
				datapoints = append(datapoints, datapoint)
				currentMetricsNum++
				// FIXME - heuristic that 10 is low enough to be accepted by OpenTSDB or env variable STATSDAEMON_MAXMETRICS
				if (currentMetricsNum%maxMetrics == 0) || idx == num-1 {
					log.Printf("currentMetricsNum: %d", currentMetricsNum)
					out, err := TSDB.Put(datapoints)
					if len(out.Errors) > 0 || err != nil {
						sout := []string{}
						for _, elem := range out.Errors {
							sout = append(sout, elem.Error)
						}
						log.Printf("OpenTSDB.Put return: %s, error: %s", strings.Join(sout, "; "), err)
					}
					if err != nil {
						return err
					}
					datapoints = []tsdb.DataPoint{}
				}
			} else {
				logCtx.WithField("after", "Split").Errorf("Buffer format. Expected \"metric value timestamp\". Got \"%s\"", mtr)
				Stat.ErrorIncr()
			}

		}

		// log.Printf("datapoints len: %d, data: %s\n", len(datapoints), StructPrettyPrint(datapoints))

		// TSDB.Servers = append(TSDB.Servers, server)
		// _, err = TSDB.Put(datapoints)
		// if err != nil {
		// 	return err
		// }

		logCtx = log.WithFields(log.Fields{
			"in":  "openTSDB",
			"ctx": "success writing to OpenTSDB",
		})
		logCtx.WithField("after", "Put").Infof("sent %d stats to %s", currentMetricsNum, config.OpenTSDBAddress)

		return nil

	}
	return fmt.Errorf("No valid OpenTSDB address: %s", config.OpenTSDBAddress)

}
Example #2
0
func submit(deadline time.Time) error {
	var buffer bytes.Buffer
	var num int64
	now := time.Now().Unix()
	num += processCounters(&buffer, now)
	num += processGauges(&buffer, now)
	num += processTimers(&buffer, now, percentThreshold)
	if *graphiteAddress != "-" {
		client, err := net.Dial("tcp", *graphiteAddress)
		if err != nil {
			if *debug {
				log.Printf("WARNING: resetting counters when in debug mode")
				processCounters(&buffer, now)
				processGauges(&buffer, now)
				processTimers(&buffer, now, percentThreshold)
			}
			return spew.Errorf("dialing %s failed - %s", *graphiteAddress, err)
		}
		defer client.Close()

		err = client.SetDeadline(deadline)
		if err != nil {
			return spew.Errorf("could not set deadline: %v", err)
		}

		if num == 0 {
			return nil
		}

		if *debug {
			for _, line := range bytes.Split(buffer.Bytes(), []byte("\n")) {
				if len(line) == 0 {
					continue
				}
				log.Printf("DEBUG: %s", line)
			}
		}

		_, err = client.Write(buffer.Bytes())
		if err != nil {
			return spew.Errorf("failed to write stats - %s", err)
		}

		log.Printf("sent %d stats to %s", num, *graphiteAddress)

	}
	if *openTSDBAddress != "-" {
		datapoints := []tsdb.DataPoint{}
		TSDB := tsdb.TSDB{}
		server := tsdb.Server{}
		serverAdress := strings.Split(*openTSDBAddress, ":")
		if len(serverAdress) != 2 {
			return spew.Errorf("Error: Incorrect openTSDB server address")
		}
		port, err := strconv.ParseUint(serverAdress[1], 10, 32)
		if err != nil {
			return spew.Errorf("%d %v: '%#v'", getCaller(), err, serverAdress[1])
		}
		server.Host = serverAdress[0]
		server.Port = uint(port)
		TSDB.Servers = append(TSDB.Servers, server)

		metrics := strings.Split(buffer.String(), "\n")

		for _, mtr := range metrics {
			data := strings.Split(mtr, " ")
			if len(data) == 3 {
				metric := tsdb.Metric{}
				value := tsdb.Value{}
				tags := tsdb.Tags{}
				timestamp := tsdb.Time{}
				datapoint := tsdb.DataPoint{}
				val, err := strconv.ParseFloat(data[1], 64)
				if err != nil {
					return spew.Errorf("%d %v: '%#v'", getCaller(), err, data[1])
				}
				value.Set(val)
				err = timestamp.Parse(data[2])
				if err != nil {
					return spew.Errorf("%d %v: '%#v'", getCaller(), err, data[2])
				}
				metricAndTags := strings.Split(data[0], "?")
				if metricAndTags[0] != data[0] {
					err = metric.Set(metricAndTags[0])
					if err != nil {
						return spew.Errorf("%d %v: '%#v'", getCaller(), err, metricAndTags[0])
					}
					for _, tagVal := range strings.Split(metricAndTags[1], "&") {
						arrTagVal := strings.Split(tagVal, "=")
						if len(arrTagVal) != 2 {
							return spew.Errorf("Error: Incorrect metric format %s: '%s'", "tagVal", tagVal)
						}
						tags.Set(arrTagVal[0], arrTagVal[1])
					}
				} else {
					metricAndTags := strings.Split(data[0], "._t_")
					if len(metricAndTags) != 2 {
						return spew.Errorf("Error: Incorrect metric format %s: '%s'", "data[0]", data[0])
					}
					err = metric.Set(metricAndTags[0])
					if err != nil {
						return spew.Errorf("%v : '%#v'", err, metricAndTags[0])
					}
					arrTagVal := strings.Split(metricAndTags[1], ".")
					if len(arrTagVal) != 2 {
						return spew.Errorf("Error: Incorrect metric format %s: '%s'", "metricAndTags[1]", metricAndTags[1])
					}
					tags.Set(arrTagVal[0], arrTagVal[1])
				}

				datapoint.Value = &value
				datapoint.Metric = &metric
				datapoint.Tags = &tags
				datapoint.Timestamp = &timestamp
				datapoints = append(datapoints, datapoint)

				if len(datapoints) > 10 {
					_, err = TSDB.Put(datapoints)
					if err != nil {
						return spew.Errorf("%d %v: '%#v'", getCaller(), err, datapoints)
					}
					datapoints = []tsdb.DataPoint{}
				}
			}

		}

		if len(datapoints) > 0 {
			_, err = TSDB.Put(datapoints)
			if err != nil {
				return spew.Errorf("%d %v: '%#v'", getCaller(), err, datapoints)
			}
		}
		log.Printf("sent %d stats to %s", num, *openTSDBAddress)

	}
	return nil
}