forked from szaydel/statsdaemon
/
opentsdb.go
149 lines (130 loc) · 4.02 KB
/
opentsdb.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// opentsdb.go
package main
import (
"bytes"
"encoding/json"
"fmt"
"os"
"strconv"
"strings"
log "github.com/Sirupsen/logrus"
"github.com/wojtekzw/go-opentsdb/tsdb"
)
const (
//environment variable SD_OTSDB_MAXMETRICS can be used to change max metrics to OpenTSDB
SD_OTSDB_MAXMETRICS = 10
)
// StructPrettyPrint - JSON like
func StructPrettyPrint(s interface{}) string {
bytesStruct, _ := json.MarshalIndent(s, "", " ")
return string(bytesStruct)
}
func openTSDB(config ConfigApp, buffer *bytes.Buffer) error {
logCtx := log.WithFields(log.Fields{
"in": "openTSDB",
})
maxMetrics := SD_OTSDB_MAXMETRICS
if i, err := strconv.Atoi(os.Getenv("SD_OTSDB_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.Errorf("Only float/integer values allowed. Got: %s in line \"%s\". Error: %s", data[1], data, err)
Stat.OtherErrorsInc()
continue
}
value.Set(val)
// parse timestamp
err = timestamp.Parse(data[2])
if err != nil {
logCtx.Errorf("Timestamp expected. Got: %s in line \"%s\". Error: %s", data[2], data, err)
Stat.OtherErrorsInc()
continue
}
// parse metric/bucket
metricName := data[0]
err = metric.Set(metricName)
if err != nil {
logCtx.Errorf("Metric name expected. Got: %s in line \"%s\". Error: %s", data[0], data, err)
Stat.OtherErrorsInc()
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.Errorf("Tag expected. Got: %s in line \"%s\"", e, data)
Stat.OtherErrorsInc()
continue
}
tags.Set(strSlice[0], strSlice[1])
}
}
}
}
datapoint.Value = &value
datapoint.Metric = &metric
datapoint.Tags = &tags
datapoint.Timestamp = ×tamp
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 {
out, err := TSDB.Put(datapoints)
if len(out.Errors) > 0 || err != nil {
sout := []string{}
for _, elem := range out.Errors {
sout = append(sout, elem.Error)
}
logCtx.Errorf("OpenTSDB.Put return: %s, error: %s", strings.Join(sout, "; "), err)
}
if err != nil {
return err
}
datapoints = []tsdb.DataPoint{}
}
} else {
logCtx.Errorf("Buffer format. Expected \"metric value timestamp\". Got \"%s\"", mtr)
Stat.OtherErrorsInc()
}
}
logCtx.Infof("sent %d stats to %s", currentMetricsNum, config.OpenTSDBAddress)
return nil
}
return fmt.Errorf("No valid OpenTSDB address: %s", config.OpenTSDBAddress)
}