github.com/timstclair/heapster@v0.20.0-alpha1/metrics/sinks/influxdb/influxdb.go (about) 1 // Copyright 2015 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package influxdb 16 17 import ( 18 "fmt" 19 "net/url" 20 "strings" 21 "sync" 22 "time" 23 24 influxdb_common "k8s.io/heapster/common/influxdb" 25 "k8s.io/heapster/metrics/core" 26 27 "github.com/golang/glog" 28 influxdb "github.com/influxdb/influxdb/client" 29 ) 30 31 type influxdbSink struct { 32 client influxdb_common.InfluxdbClient 33 sync.RWMutex 34 c influxdb_common.InfluxdbConfig 35 dbExists bool 36 } 37 38 const ( 39 // Value Field name 40 valueField = "value" 41 // Event special tags 42 dbNotFoundError = "database not found" 43 44 // Maximum number of influxdb Points to be sent in one batch. 45 maxSendBatchSize = 10000 46 ) 47 48 func (sink *influxdbSink) resetConnection() { 49 glog.Infof("Influxdb connection reset") 50 sink.dbExists = false 51 sink.client = nil 52 } 53 54 func (sink *influxdbSink) ExportData(dataBatch *core.DataBatch) { 55 sink.Lock() 56 defer sink.Unlock() 57 58 dataPoints := make([]influxdb.Point, 0, 0) 59 for _, metricSet := range dataBatch.MetricSets { 60 for metricName, metricValue := range metricSet.MetricValues { 61 62 var value interface{} 63 if core.ValueInt64 == metricValue.ValueType { 64 value = metricValue.IntValue 65 } else if core.ValueFloat == metricValue.ValueType { 66 value = metricValue.FloatValue 67 } else { 68 continue 69 } 70 71 point := influxdb.Point{ 72 Measurement: metricName, 73 Tags: metricSet.Labels, 74 Fields: map[string]interface{}{ 75 "value": value, 76 }, 77 Time: dataBatch.Timestamp.UTC(), 78 } 79 dataPoints = append(dataPoints, point) 80 if len(dataPoints) >= maxSendBatchSize { 81 sink.sendData(dataPoints) 82 dataPoints = make([]influxdb.Point, 0, 0) 83 } 84 } 85 } 86 if len(dataPoints) >= 0 { 87 sink.sendData(dataPoints) 88 } 89 } 90 91 func (sink *influxdbSink) sendData(dataPoints []influxdb.Point) { 92 if err := sink.createDatabase(); err != nil { 93 glog.Errorf("Failed to create infuxdb: %v", err) 94 } 95 bp := influxdb.BatchPoints{ 96 Points: dataPoints, 97 Database: sink.c.DbName, 98 RetentionPolicy: "default", 99 } 100 101 start := time.Now() 102 if _, err := sink.client.Write(bp); err != nil { 103 if strings.Contains(err.Error(), dbNotFoundError) { 104 sink.resetConnection() 105 } else if _, _, err := sink.client.Ping(); err != nil { 106 glog.Errorf("InfluxDB ping failed: %v", err) 107 sink.resetConnection() 108 } 109 } 110 end := time.Now() 111 glog.V(4).Info("Exported %d data to influxDB in %s", len(dataPoints), end.Sub(start)) 112 } 113 114 func (sink *influxdbSink) Name() string { 115 return "InfluxDB Sink" 116 } 117 118 func (sink *influxdbSink) Stop() { 119 // nothing needs to be done. 120 } 121 122 func (sink *influxdbSink) createDatabase() error { 123 if sink.client == nil { 124 client, err := influxdb_common.NewClient(sink.c) 125 if err != nil { 126 return err 127 } 128 sink.client = client 129 } 130 131 if sink.dbExists { 132 return nil 133 } 134 q := influxdb.Query{ 135 Command: fmt.Sprintf("CREATE DATABASE %s", sink.c.DbName), 136 } 137 if resp, err := sink.client.Query(q); err != nil { 138 if !(resp != nil && resp.Err != nil && strings.Contains(resp.Err.Error(), "already exists")) { 139 return fmt.Errorf("Database creation failed: %v", err) 140 } 141 } 142 sink.dbExists = true 143 glog.Infof("Created database %q on influxDB server at %q", sink.c.DbName, sink.c.Host) 144 return nil 145 } 146 147 // Returns a thread-compatible implementation of influxdb interactions. 148 func new(c influxdb_common.InfluxdbConfig) core.DataSink { 149 client, err := influxdb_common.NewClient(c) 150 if err != nil { 151 glog.Errorf("issues while creating an InfluxDB sink: %v, will retry on use", err) 152 } 153 return &influxdbSink{ 154 client: client, // can be nil 155 c: c, 156 } 157 } 158 159 func CreateInfluxdbSink(uri *url.URL) (core.DataSink, error) { 160 config, err := influxdb_common.BuildConfig(uri) 161 if err != nil { 162 return nil, err 163 } 164 sink := new(*config) 165 glog.Infof("created influxdb sink with options: host:%s user:%s db:%s", config.Host, config.User, config.DbName) 166 return sink, nil 167 }