github.com/timstclair/heapster@v0.20.0-alpha1/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  	"runtime"
    20  	"strconv"
    21  	"sync"
    22  
    23  	"time"
    24  
    25  	riemann_api "github.com/bigdatadev/goryman"
    26  	"github.com/golang/glog"
    27  	"k8s.io/heapster/metrics/core"
    28  )
    29  
    30  // Abstracted for testing: this package works against any client that obeys the
    31  // interface contract exposed by the goryman Riemann client
    32  
    33  type riemannClient interface {
    34  	Connect() error
    35  	Close() error
    36  	SendEvent(e *riemann_api.Event) error
    37  }
    38  
    39  type riemannSink struct {
    40  	client riemannClient
    41  	config riemannConfig
    42  	sync.RWMutex
    43  }
    44  
    45  type riemannConfig struct {
    46  	host  string
    47  	ttl   float32
    48  	state string
    49  	tags  []string
    50  }
    51  
    52  const (
    53  	// Maximum number of riemann Events to be sent in one batch.
    54  	maxSendBatchSize = 10000
    55  )
    56  
    57  func CreateRiemannSink(uri *url.URL) (core.DataSink, error) {
    58  	c := riemannConfig{
    59  		host:  "riemann-heapster:5555",
    60  		ttl:   60.0,
    61  		state: "",
    62  		tags:  make([]string, 0),
    63  	}
    64  	if len(uri.Host) > 0 {
    65  		c.host = uri.Host
    66  	}
    67  	options := uri.Query()
    68  	if len(options["ttl"]) > 0 {
    69  		var ttl, err = strconv.ParseFloat(options["ttl"][0], 32)
    70  		if err != nil {
    71  			return nil, err
    72  		}
    73  		c.ttl = float32(ttl)
    74  	}
    75  	if len(options["state"]) > 0 {
    76  		c.state = options["state"][0]
    77  	}
    78  	if len(options["tags"]) > 0 {
    79  		c.tags = options["tags"]
    80  	}
    81  
    82  	glog.Infof("Riemann sink URI: '%+v', host: '%+v', options: '%+v', ", uri, c.host, options)
    83  	rs := &riemannSink{
    84  		client: riemann_api.NewGorymanClient(c.host),
    85  		config: c,
    86  	}
    87  
    88  	err := rs.setupRiemannClient()
    89  	if err != nil {
    90  		return nil, err
    91  	}
    92  
    93  	runtime.SetFinalizer(rs.client, func(c riemannClient) { c.Close() })
    94  	return rs, nil
    95  }
    96  
    97  func (rs *riemannSink) setupRiemannClient() error {
    98  	return rs.client.Connect()
    99  }
   100  
   101  // Return a user-friendly string describing the sink
   102  func (sink *riemannSink) Name() string {
   103  	return "Riemann Sink"
   104  }
   105  
   106  func (sink *riemannSink) Stop() {
   107  	// nothing needs to be done.
   108  }
   109  
   110  // ExportData Send a collection of Timeseries to Riemann
   111  func (sink *riemannSink) ExportData(dataBatch *core.DataBatch) {
   112  	sink.Lock()
   113  	defer sink.Unlock()
   114  	dataEvents := make([]riemann_api.Event, 0, 0)
   115  	for _, metricSet := range dataBatch.MetricSets {
   116  		for metricName, metricValue := range metricSet.MetricValues {
   117  			var value interface{}
   118  			if core.ValueInt64 == metricValue.ValueType {
   119  				value = metricValue.IntValue
   120  			} else if core.ValueFloat == metricValue.ValueType {
   121  				value = metricValue.FloatValue
   122  			} else {
   123  				continue
   124  			}
   125  
   126  			//get the value of "hostname" key
   127  			host := ""
   128  			for key, value := range metricSet.Labels {
   129  				if key == "hostname" {
   130  					host = value
   131  				}
   132  			}
   133  
   134  			//get the metrics description
   135  			description := ""
   136  			for _, standardMetrics := range core.StandardMetrics {
   137  				if standardMetrics.MetricDescriptor.Name == metricName {
   138  					description = standardMetrics.MetricDescriptor.Description
   139  				}
   140  			}
   141  
   142  			event := riemann_api.Event{
   143  				Time:        dataBatch.Timestamp.Unix(),
   144  				Service:     metricName,
   145  				Host:        host,
   146  				Description: description,
   147  				Attributes:  metricSet.Labels,
   148  				Metric:      value,
   149  				Ttl:         sink.config.ttl,
   150  				State:       sink.config.state,
   151  				Tags:        sink.config.tags,
   152  			}
   153  
   154  			dataEvents = append(dataEvents, event)
   155  			if len(dataEvents) >= maxSendBatchSize {
   156  				sink.sendData(dataEvents)
   157  				dataEvents = make([]riemann_api.Event, 0, 0)
   158  			}
   159  
   160  		}
   161  	}
   162  
   163  	if len(dataEvents) >= 0 {
   164  		sink.sendData(dataEvents)
   165  	}
   166  }
   167  
   168  func (sink *riemannSink) sendData(dataEvents []riemann_api.Event) {
   169  	start := time.Now()
   170  	for _, event := range dataEvents {
   171  		err := sink.client.SendEvent(&event)
   172  		if err != nil {
   173  			glog.V(2).Infof("Failed sending event to Riemann: %+v: %+v", event, err)
   174  		}
   175  	}
   176  	end := time.Now()
   177  	glog.V(4).Info("Exported %d data to riemann in %s", len(dataEvents), end.Sub(start))
   178  }