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 }