github.com/jonaz/heapster@v1.3.0-beta.0.0.20170208112634-cd3c15ca3d29/metrics/sinks/riemann/driver.go (about)

     1  // Copyright 2014 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 riemann
    16  
    17  import (
    18  	"net/url"
    19  	"reflect"
    20  	"runtime"
    21  	"strconv"
    22  	"sync"
    23  
    24  	"time"
    25  
    26  	"github.com/golang/glog"
    27  	riemann_api "github.com/rikatz/goryman"
    28  	"k8s.io/heapster/metrics/core"
    29  )
    30  
    31  // Abstracted for testing: this package works against any client that obeys the
    32  // interface contract exposed by the goryman Riemann client
    33  
    34  type riemannClient interface {
    35  	Connect() error
    36  	Close() error
    37  	SendEvent(e *riemann_api.Event) error
    38  }
    39  
    40  type riemannSink struct {
    41  	client riemannClient
    42  	config riemannConfig
    43  	sync.RWMutex
    44  }
    45  
    46  type riemannConfig struct {
    47  	host  string
    48  	ttl   float32
    49  	state string
    50  	tags  []string
    51  }
    52  
    53  const (
    54  	// Maximum number of riemann Events to be sent in one batch.
    55  	maxSendBatchSize = 10000
    56  	max_retries      = 2
    57  )
    58  
    59  func CreateRiemannSink(uri *url.URL) (core.DataSink, error) {
    60  	c := riemannConfig{
    61  		host:  "riemann-heapster:5555",
    62  		ttl:   60.0,
    63  		state: "",
    64  		tags:  make([]string, 0),
    65  	}
    66  	if len(uri.Host) > 0 {
    67  		c.host = uri.Host
    68  	}
    69  	options := uri.Query()
    70  	if len(options["ttl"]) > 0 {
    71  		var ttl, err = strconv.ParseFloat(options["ttl"][0], 32)
    72  		if err != nil {
    73  			return nil, err
    74  		}
    75  		c.ttl = float32(ttl)
    76  	}
    77  	if len(options["state"]) > 0 {
    78  		c.state = options["state"][0]
    79  	}
    80  	if len(options["tags"]) > 0 {
    81  		c.tags = options["tags"]
    82  	}
    83  
    84  	glog.Infof("Riemann sink URI: '%+v', host: '%+v', options: '%+v', ", uri, c.host, options)
    85  	rs := &riemannSink{
    86  		client: nil,
    87  		config: c,
    88  	}
    89  
    90  	err := rs.setupRiemannClient()
    91  	if err != nil {
    92  		glog.Warningf("Riemann sink not connected: %v", err)
    93  		// Warn but return the sink.
    94  	}
    95  	return rs, nil
    96  }
    97  
    98  func (rs *riemannSink) setupRiemannClient() error {
    99  	client := riemann_api.NewGorymanClient(rs.config.host)
   100  	runtime.SetFinalizer(client, func(c riemannClient) { c.Close() })
   101  	err := client.Connect()
   102  	if err != nil {
   103  		return err
   104  	}
   105  	rs.client = client
   106  	return nil
   107  }
   108  
   109  // Return a user-friendly string describing the sink
   110  func (sink *riemannSink) Name() string {
   111  	return "Riemann Sink"
   112  }
   113  
   114  func (sink *riemannSink) Stop() {
   115  	// nothing needs to be done.
   116  }
   117  
   118  // ExportData Send a collection of Timeseries to Riemann
   119  func (sink *riemannSink) ExportData(dataBatch *core.DataBatch) {
   120  	sink.Lock()
   121  	defer sink.Unlock()
   122  
   123  	if sink.client == nil {
   124  		if err := sink.setupRiemannClient(); err != nil {
   125  			glog.Warningf("Riemann sink not connected: %v", err)
   126  			return
   127  		}
   128  	}
   129  
   130  	var dataEvents []riemann_api.Event
   131  	appendMetric := func(host, name string, value interface{}, labels map[string]string) {
   132  		event := riemann_api.Event{
   133  			Time:        dataBatch.Timestamp.Unix(),
   134  			Service:     name,
   135  			Host:        host,
   136  			Description: "", //no description - waste of bandwidth.
   137  			Attributes:  labels,
   138  			Metric:      value,
   139  			Ttl:         sink.config.ttl,
   140  			State:       sink.config.state,
   141  			Tags:        sink.config.tags,
   142  		}
   143  
   144  		dataEvents = append(dataEvents, event)
   145  		if len(dataEvents) >= maxSendBatchSize {
   146  			sink.sendData(dataEvents)
   147  			dataEvents = nil
   148  		}
   149  	}
   150  
   151  	riemannValue := func(value interface{}) interface{} {
   152  		// Workaround for error from goryman: "Metric of invalid type (type int64)"
   153  		if reflect.TypeOf(value).Kind() == reflect.Int64 {
   154  			return int(value.(int64))
   155  		}
   156  		return value
   157  	}
   158  
   159  	for _, metricSet := range dataBatch.MetricSets {
   160  		host := metricSet.Labels[core.LabelHostname.Key]
   161  		for metricName, metricValue := range metricSet.MetricValues {
   162  			if value := metricValue.GetValue(); value != nil {
   163  				appendMetric(host, metricName, riemannValue(value), metricSet.Labels)
   164  			}
   165  		}
   166  		for _, metric := range metricSet.LabeledMetrics {
   167  			if value := metric.GetValue(); value != nil {
   168  				labels := make(map[string]string)
   169  				for k, v := range metricSet.Labels {
   170  					labels[k] = v
   171  				}
   172  				for k, v := range metric.Labels {
   173  					labels[k] = v
   174  				}
   175  				appendMetric(host, metric.Name, riemannValue(value), labels)
   176  			}
   177  		}
   178  	}
   179  
   180  	if len(dataEvents) > 0 {
   181  		sink.sendData(dataEvents)
   182  	}
   183  }
   184  
   185  func (sink *riemannSink) sendData(dataEvents []riemann_api.Event) {
   186  	if sink.client == nil {
   187  		return
   188  	}
   189  
   190  	start := time.Now()
   191  	errors := 0
   192  	for _, event := range dataEvents {
   193  		glog.V(8).Infof("Sending event to Riemann:  %+v", event)
   194  		var err error
   195  		for try := 0; try < max_retries; try++ {
   196  			err = sink.client.SendEvent(&event)
   197  			if err == nil {
   198  				break
   199  			}
   200  		}
   201  		if err != nil {
   202  			errors++
   203  			glog.V(4).Infof("Failed to send event to Riemann: %+v: %+v", event, err)
   204  		}
   205  	}
   206  	end := time.Now()
   207  	if errors > 0 {
   208  		glog.V(2).Info("There were errors sending events to Riemman, forcing reconnection")
   209  		sink.client.Close()
   210  		sink.client = nil
   211  	}
   212  	glog.V(4).Infof("Exported %d events to riemann in %s", len(dataEvents)-errors, end.Sub(start))
   213  }