github.com/aclisp/heapster@v0.19.2-0.20160613100040-51756f899a96/metrics/sinks/metric/metric_sink.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 metricsink 16 17 import ( 18 "sync" 19 "time" 20 21 "k8s.io/heapster/metrics/core" 22 ) 23 24 // A simple in-memory storage for metrics. It divides metrics into 2 categories 25 // * metrics that need to be stored for couple minutes. 26 // * metrics that need to be stored for longer time (15 min, 1 hour). 27 // The user of this struct needs to decide what are the long-stored metrics uprfront. 28 type MetricSink struct { 29 // Request can come from other threads. 30 lock sync.Mutex 31 32 // List of metrics that will be stored for up to X seconds. 33 longStoreMetrics []string 34 longStoreDuration time.Duration 35 shortStoreDuration time.Duration 36 37 // Stores full DataBatch with all metrics and labels. 38 shortStore []*core.DataBatch 39 // Memory-efficient long/mid term storage for metrics. 40 longStore []*multimetricStore 41 } 42 43 // Stores values of a single metrics for different MetricSets. 44 // Assumes that the user knows what the metric is. 45 type int64Store map[string]int64 46 47 type multimetricStore struct { 48 // Timestamp of the batch from which the metrics were taken. 49 timestamp time.Time 50 // Metric name to int64store with metric values. 51 store map[string]int64Store 52 } 53 54 type TimestampedMetricValue struct { 55 core.MetricValue 56 Timestamp time.Time 57 } 58 59 func buildMultimetricStore(metrics []string, batch *core.DataBatch) *multimetricStore { 60 store := multimetricStore{ 61 timestamp: batch.Timestamp, 62 store: make(map[string]int64Store, len(metrics)), 63 } 64 for _, metric := range metrics { 65 store.store[metric] = make(int64Store, len(batch.MetricSets)) 66 } 67 for key, ms := range batch.MetricSets { 68 for _, metric := range metrics { 69 if metricValue, found := ms.MetricValues[metric]; found { 70 metricstore := store.store[metric] 71 metricstore[key] = metricValue.IntValue 72 } 73 } 74 } 75 return &store 76 } 77 78 func (this *MetricSink) Name() string { 79 return "Metric Sink" 80 } 81 82 func (this *MetricSink) Stop() { 83 // Do nothing. 84 } 85 86 func (this *MetricSink) ExportData(batch *core.DataBatch) { 87 this.lock.Lock() 88 defer this.lock.Unlock() 89 90 now := time.Now() 91 // TODO: add sorting 92 this.longStore = append(popOldStore(this.longStore, now.Add(-this.longStoreDuration)), 93 buildMultimetricStore(this.longStoreMetrics, batch)) 94 this.shortStore = append(popOld(this.shortStore, now.Add(-this.shortStoreDuration)), batch) 95 } 96 97 func (this *MetricSink) GetLatestDataBatch() *core.DataBatch { 98 this.lock.Lock() 99 defer this.lock.Unlock() 100 101 if len(this.shortStore) == 0 { 102 return nil 103 } 104 return this.shortStore[len(this.shortStore)-1] 105 } 106 107 func (this *MetricSink) GetShortStore() []*core.DataBatch { 108 this.lock.Lock() 109 defer this.lock.Unlock() 110 111 result := make([]*core.DataBatch, 0, len(this.shortStore)) 112 for _, batch := range this.shortStore { 113 result = append(result, batch) 114 } 115 return result 116 } 117 118 func (this *MetricSink) GetMetric(metricName string, keys []string, start, end time.Time) map[string][]TimestampedMetricValue { 119 this.lock.Lock() 120 defer this.lock.Unlock() 121 122 useLongStore := false 123 for _, longStoreMetric := range this.longStoreMetrics { 124 if longStoreMetric == metricName { 125 useLongStore = true 126 } 127 } 128 129 result := make(map[string][]TimestampedMetricValue) 130 if useLongStore { 131 for _, store := range this.longStore { 132 // Inclusive start and end. 133 if !store.timestamp.Before(start) && !store.timestamp.After(end) { 134 substore := store.store[metricName] 135 for _, key := range keys { 136 if val, found := substore[key]; found { 137 result[key] = append(result[key], TimestampedMetricValue{ 138 Timestamp: store.timestamp, 139 MetricValue: core.MetricValue{ 140 IntValue: val, 141 ValueType: core.ValueInt64, 142 MetricType: core.MetricGauge, 143 }, 144 }) 145 } 146 } 147 } 148 } 149 } else { 150 for _, batch := range this.shortStore { 151 // Inclusive start and end. 152 if !batch.Timestamp.Before(start) && !batch.Timestamp.After(end) { 153 for _, key := range keys { 154 metricSet, found := batch.MetricSets[key] 155 if !found { 156 continue 157 } 158 metricValue, found := metricSet.MetricValues[metricName] 159 if !found { 160 continue 161 } 162 keyResult, found := result[key] 163 if !found { 164 keyResult = make([]TimestampedMetricValue, 0) 165 } 166 keyResult = append(keyResult, TimestampedMetricValue{ 167 Timestamp: batch.Timestamp, 168 MetricValue: metricValue, 169 }) 170 result[key] = keyResult 171 } 172 } 173 } 174 } 175 return result 176 } 177 178 func (this *MetricSink) GetMetricNames(key string) []string { 179 this.lock.Lock() 180 defer this.lock.Unlock() 181 182 metricNames := make(map[string]bool) 183 for _, batch := range this.shortStore { 184 if set, found := batch.MetricSets[key]; found { 185 for key := range set.MetricValues { 186 metricNames[key] = true 187 } 188 } 189 } 190 result := make([]string, 0, len(metricNames)) 191 for key := range metricNames { 192 result = append(result, key) 193 } 194 return result 195 } 196 197 func (this *MetricSink) getAllNames(predicate func(ms *core.MetricSet) bool, 198 name func(key string, ms *core.MetricSet) string) []string { 199 this.lock.Lock() 200 defer this.lock.Unlock() 201 202 if len(this.shortStore) == 0 { 203 return []string{} 204 } 205 206 result := make([]string, 0, 0) 207 for key, value := range this.shortStore[len(this.shortStore)-1].MetricSets { 208 if predicate(value) { 209 result = append(result, name(key, value)) 210 } 211 } 212 return result 213 } 214 215 /* 216 * For debugging only. 217 */ 218 func (this *MetricSink) GetMetricSetKeys() []string { 219 return this.getAllNames( 220 func(ms *core.MetricSet) bool { return true }, 221 func(key string, ms *core.MetricSet) string { return key }) 222 } 223 224 func (this *MetricSink) GetNodes() []string { 225 return this.getAllNames( 226 func(ms *core.MetricSet) bool { return ms.Labels[core.LabelMetricSetType.Key] == core.MetricSetTypeNode }, 227 func(key string, ms *core.MetricSet) string { return ms.Labels[core.LabelHostname.Key] }) 228 } 229 230 func (this *MetricSink) GetPods() []string { 231 return this.getAllNames( 232 func(ms *core.MetricSet) bool { return ms.Labels[core.LabelMetricSetType.Key] == core.MetricSetTypePod }, 233 func(key string, ms *core.MetricSet) string { 234 return ms.Labels[core.LabelNamespaceName.Key] + "/" + ms.Labels[core.LabelPodName.Key] 235 }) 236 } 237 238 func (this *MetricSink) GetNamespaces() []string { 239 return this.getAllNames( 240 func(ms *core.MetricSet) bool { 241 return ms.Labels[core.LabelMetricSetType.Key] == core.MetricSetTypeNamespace 242 }, 243 func(key string, ms *core.MetricSet) string { return ms.Labels[core.LabelNamespaceName.Key] }) 244 } 245 246 func (this *MetricSink) GetPodsFromNamespace(namespace string) []string { 247 return this.getAllNames( 248 func(ms *core.MetricSet) bool { 249 return ms.Labels[core.LabelMetricSetType.Key] == core.MetricSetTypePod && 250 ms.Labels[core.LabelNamespaceName.Key] == namespace 251 }, 252 func(key string, ms *core.MetricSet) string { 253 return ms.Labels[core.LabelPodName.Key] 254 }) 255 } 256 257 func (this *MetricSink) GetContainersForPodFromNamespace(namespace, pod string) []string { 258 return this.getAllNames( 259 func(ms *core.MetricSet) bool { 260 return ms.Labels[core.LabelMetricSetType.Key] == core.MetricSetTypePodContainer && 261 ms.Labels[core.LabelNamespaceName.Key] == namespace && 262 ms.Labels[core.LabelPodName.Key] == pod 263 }, 264 func(key string, ms *core.MetricSet) string { 265 return ms.Labels[core.LabelContainerName.Key] 266 }) 267 } 268 269 func (this *MetricSink) GetSystemContainersFromNode(node string) []string { 270 return this.getAllNames( 271 func(ms *core.MetricSet) bool { 272 return ms.Labels[core.LabelMetricSetType.Key] == core.MetricSetTypeSystemContainer && 273 ms.Labels[core.LabelHostname.Key] == node 274 }, 275 func(key string, ms *core.MetricSet) string { 276 return ms.Labels[core.LabelContainerName.Key] 277 }) 278 } 279 280 func popOld(storage []*core.DataBatch, cutoffTime time.Time) []*core.DataBatch { 281 result := make([]*core.DataBatch, 0) 282 for _, batch := range storage { 283 if batch.Timestamp.After(cutoffTime) { 284 result = append(result, batch) 285 } 286 } 287 return result 288 } 289 290 func popOldStore(storages []*multimetricStore, cutoffTime time.Time) []*multimetricStore { 291 result := make([]*multimetricStore, 0, len(storages)) 292 for _, store := range storages { 293 if store.timestamp.After(cutoffTime) { 294 result = append(result, store) 295 } 296 } 297 return result 298 } 299 300 func NewMetricSink(shortStoreDuration, longStoreDuration time.Duration, longStoreMetrics []string) *MetricSink { 301 return &MetricSink{ 302 longStoreMetrics: longStoreMetrics, 303 longStoreDuration: longStoreDuration, 304 shortStoreDuration: shortStoreDuration, 305 longStore: make([]*multimetricStore, 0), 306 shortStore: make([]*core.DataBatch, 0), 307 } 308 }