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  }