github.com/galamsiva2020/kubernetes-heapster-monitoring@v0.0.0-20210823134957-3c1baa7c1e70/events/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 "encoding/json" 19 "fmt" 20 "net/url" 21 "strings" 22 "sync" 23 "time" 24 25 kube_api "k8s.io/api/core/v1" 26 influxdb_common "k8s.io/heapster/common/influxdb" 27 "k8s.io/heapster/events/core" 28 metrics_core "k8s.io/heapster/metrics/core" 29 30 "github.com/golang/glog" 31 influxdb "github.com/influxdata/influxdb/client" 32 ) 33 34 type influxdbSink struct { 35 client influxdb_common.InfluxdbClient 36 sync.RWMutex 37 c influxdb_common.InfluxdbConfig 38 dbExists bool 39 } 40 41 const ( 42 eventMeasurementName = "log/events" 43 // Event special tags 44 eventUID = "uid" 45 // Value Field name 46 valueField = "value" 47 // Event special tags 48 dbNotFoundError = "database not found" 49 50 // Maximum number of influxdb Points to be sent in one batch. 51 maxSendBatchSize = 1000 52 ) 53 54 func (sink *influxdbSink) resetConnection() { 55 glog.Infof("Influxdb connection reset") 56 sink.dbExists = false 57 sink.client = nil 58 } 59 60 // Generate point value for event 61 func getEventValue(event *kube_api.Event) (string, error) { 62 // TODO: check whether indenting is required. 63 bytes, err := json.MarshalIndent(event, "", " ") 64 if err != nil { 65 return "", err 66 } 67 return string(bytes), nil 68 } 69 70 func eventToPointWithFields(event *kube_api.Event) (*influxdb.Point, error) { 71 point := influxdb.Point{ 72 Measurement: "events", 73 Time: event.LastTimestamp.Time.UTC(), 74 Fields: map[string]interface{}{ 75 "message": event.Message, 76 }, 77 Tags: map[string]string{ 78 eventUID: string(event.UID), 79 }, 80 } 81 if event.InvolvedObject.Kind == "Pod" { 82 point.Tags[metrics_core.LabelPodId.Key] = string(event.InvolvedObject.UID) 83 } 84 point.Tags["object_name"] = event.InvolvedObject.Name 85 point.Tags["type"] = event.Type 86 point.Tags["kind"] = event.InvolvedObject.Kind 87 point.Tags["component"] = event.Source.Component 88 point.Tags["reason"] = event.Reason 89 point.Tags[metrics_core.LabelNamespaceName.Key] = event.Namespace 90 point.Tags[metrics_core.LabelHostname.Key] = event.Source.Host 91 return &point, nil 92 } 93 94 func eventToPoint(event *kube_api.Event) (*influxdb.Point, error) { 95 value, err := getEventValue(event) 96 if err != nil { 97 return nil, err 98 } 99 100 point := influxdb.Point{ 101 Measurement: eventMeasurementName, 102 Time: event.LastTimestamp.Time.UTC(), 103 Fields: map[string]interface{}{ 104 valueField: value, 105 }, 106 Tags: map[string]string{ 107 eventUID: string(event.UID), 108 }, 109 } 110 if event.InvolvedObject.Kind == "Pod" { 111 point.Tags[metrics_core.LabelPodId.Key] = string(event.InvolvedObject.UID) 112 point.Tags[metrics_core.LabelPodName.Key] = event.InvolvedObject.Name 113 } 114 point.Tags[metrics_core.LabelHostname.Key] = event.Source.Host 115 return &point, nil 116 } 117 118 func (sink *influxdbSink) ExportEvents(eventBatch *core.EventBatch) { 119 sink.Lock() 120 defer sink.Unlock() 121 122 dataPoints := make([]influxdb.Point, 0, 10) 123 for _, event := range eventBatch.Events { 124 var point *influxdb.Point 125 var err error 126 if sink.c.WithFields { 127 point, err = eventToPointWithFields(event) 128 } else { 129 point, err = eventToPoint(event) 130 } 131 if err != nil { 132 glog.Warningf("Failed to convert event to point: %v", err) 133 } 134 135 point.Tags["cluster_name"] = sink.c.ClusterName 136 137 dataPoints = append(dataPoints, *point) 138 if len(dataPoints) >= maxSendBatchSize { 139 sink.sendData(dataPoints) 140 dataPoints = make([]influxdb.Point, 0, 1) 141 } 142 } 143 if len(dataPoints) >= 0 { 144 sink.sendData(dataPoints) 145 } 146 } 147 148 func (sink *influxdbSink) sendData(dataPoints []influxdb.Point) { 149 if err := sink.createDatabase(); err != nil { 150 glog.Errorf("Failed to create influxdb: %v", err) 151 return 152 } 153 bp := influxdb.BatchPoints{ 154 Points: dataPoints, 155 Database: sink.c.DbName, 156 RetentionPolicy: "default", 157 } 158 159 start := time.Now() 160 if _, err := sink.client.Write(bp); err != nil { 161 glog.Errorf("InfluxDB write failed: %v", err) 162 if strings.Contains(err.Error(), dbNotFoundError) { 163 sink.resetConnection() 164 } else if _, _, err := sink.client.Ping(); err != nil { 165 glog.Errorf("InfluxDB ping failed: %v", err) 166 sink.resetConnection() 167 } 168 } 169 end := time.Now() 170 glog.V(4).Infof("Exported %d data to influxDB in %s", len(dataPoints), end.Sub(start)) 171 } 172 173 func (sink *influxdbSink) Name() string { 174 return "InfluxDB Sink" 175 } 176 177 func (sink *influxdbSink) Stop() { 178 // nothing needs to be done. 179 } 180 181 func (sink *influxdbSink) createDatabase() error { 182 if sink.client == nil { 183 client, err := influxdb_common.NewClient(sink.c) 184 if err != nil { 185 return err 186 } 187 sink.client = client 188 } 189 190 if sink.dbExists { 191 return nil 192 } 193 194 q := influxdb.Query{ 195 Command: fmt.Sprintf(`CREATE DATABASE %s WITH NAME "default"`, sink.c.DbName), 196 } 197 198 if resp, err := sink.client.Query(q); err != nil { 199 // We want to return error only if it is not "already exists" error. 200 if !(resp != nil && resp.Err != nil && strings.Contains(resp.Err.Error(), "existing policy")) { 201 err := sink.createRetentionPolicy() 202 if err != nil { 203 return err 204 } 205 } 206 } 207 208 sink.dbExists = true 209 glog.Infof("Created database %q on influxDB server at %q", sink.c.DbName, sink.c.Host) 210 return nil 211 } 212 213 func (sink *influxdbSink) createRetentionPolicy() error { 214 q := influxdb.Query{ 215 Command: fmt.Sprintf(`CREATE RETENTION POLICY "default" ON %s DURATION 0d REPLICATION 1 DEFAULT`, sink.c.DbName), 216 } 217 218 if resp, err := sink.client.Query(q); err != nil { 219 // We want to return error only if it is not "already exists" error. 220 if !(resp != nil && resp.Err != nil && strings.Contains(resp.Err.Error(), "already exists")) { 221 return fmt.Errorf("Retention policy creation failed: %v", err) 222 } 223 } 224 225 glog.Infof("Created database %q on influxDB server at %q", sink.c.DbName, sink.c.Host) 226 return nil 227 } 228 229 // Returns a thread-safe implementation of core.EventSink for InfluxDB. 230 func newSink(c influxdb_common.InfluxdbConfig) core.EventSink { 231 client, err := influxdb_common.NewClient(c) 232 if err != nil { 233 glog.Errorf("issues while creating an InfluxDB sink: %v, will retry on use", err) 234 } 235 return &influxdbSink{ 236 client: client, // can be nil 237 c: c, 238 } 239 } 240 241 func CreateInfluxdbSink(uri *url.URL) (core.EventSink, error) { 242 config, err := influxdb_common.BuildConfig(uri) 243 if err != nil { 244 return nil, err 245 } 246 sink := newSink(*config) 247 glog.Infof("created influxdb sink with options: host:%s user:%s db:%s", config.Host, config.User, config.DbName) 248 return sink, nil 249 }