github.com/timstclair/heapster@v0.20.0-alpha1/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 // Stores timmed DataBatches with selected metrics and no labels 40 longStore []*core.DataBatch 41 } 42 43 type TimestampedMetricValue struct { 44 core.MetricValue 45 Timestamp time.Time 46 } 47 48 func (this *MetricSink) Name() string { 49 return "MetricSink" 50 } 51 52 func (this *MetricSink) Stop() { 53 // Do nothing. 54 } 55 56 func (this *MetricSink) ExportData(batch *core.DataBatch) { 57 trimmed := this.trimDataBatch(batch) 58 59 this.lock.Lock() 60 defer this.lock.Unlock() 61 62 now := time.Now() 63 // TODO: add sorting 64 this.longStore = append(popOld(this.longStore, now.Add(-this.longStoreDuration)), trimmed) 65 this.shortStore = append(popOld(this.shortStore, now.Add(-this.shortStoreDuration)), batch) 66 } 67 68 func (this *MetricSink) GetShortStore() []*core.DataBatch { 69 this.lock.Lock() 70 defer this.lock.Unlock() 71 72 result := make([]*core.DataBatch, 0, len(this.shortStore)) 73 for _, batch := range this.shortStore { 74 result = append(result, batch) 75 } 76 return result 77 } 78 79 func (this *MetricSink) GetMetric(metricName string, keys []string, start, end time.Time) map[string][]TimestampedMetricValue { 80 this.lock.Lock() 81 defer this.lock.Unlock() 82 83 var storeToUse []*core.DataBatch = nil 84 for _, longStoreMetric := range this.longStoreMetrics { 85 if longStoreMetric == metricName { 86 storeToUse = this.longStore 87 } 88 } 89 if storeToUse == nil { 90 storeToUse = this.shortStore 91 } 92 93 result := make(map[string][]TimestampedMetricValue) 94 95 for _, batch := range storeToUse { 96 // Inclusive start and end. 97 if !batch.Timestamp.Before(start) && !batch.Timestamp.After(end) { 98 for _, key := range keys { 99 metricSet, found := batch.MetricSets[key] 100 if !found { 101 continue 102 } 103 metricValue, found := metricSet.MetricValues[metricName] 104 if !found { 105 continue 106 } 107 keyResult, found := result[key] 108 if !found { 109 keyResult = make([]TimestampedMetricValue, 0) 110 } 111 keyResult = append(keyResult, TimestampedMetricValue{ 112 Timestamp: batch.Timestamp, 113 MetricValue: metricValue, 114 }) 115 result[key] = keyResult 116 } 117 } 118 } 119 return result 120 } 121 122 func (this *MetricSink) GetMetricNames(key string) []string { 123 this.lock.Lock() 124 defer this.lock.Unlock() 125 126 metricNames := make(map[string]bool) 127 for _, batch := range this.longStore { 128 if set, found := batch.MetricSets[key]; found { 129 for key := range set.MetricValues { 130 metricNames[key] = true 131 } 132 } 133 } 134 for _, batch := range this.shortStore { 135 if set, found := batch.MetricSets[key]; found { 136 for key := range set.MetricValues { 137 metricNames[key] = true 138 } 139 } 140 } 141 result := make([]string, 0, len(metricNames)) 142 for key := range metricNames { 143 result = append(result, key) 144 } 145 return result 146 } 147 148 func (this *MetricSink) getAllNames(predicate func(ms *core.MetricSet) bool, 149 name func(key string, ms *core.MetricSet) string) []string { 150 this.lock.Lock() 151 defer this.lock.Unlock() 152 153 if len(this.shortStore) == 0 { 154 return []string{} 155 } 156 157 result := make([]string, 0, 0) 158 for key, value := range this.shortStore[len(this.shortStore)-1].MetricSets { 159 if predicate(value) { 160 result = append(result, name(key, value)) 161 } 162 } 163 return result 164 } 165 166 /* 167 * For debugging only. 168 */ 169 func (this *MetricSink) GetMetricSetKeys() []string { 170 return this.getAllNames( 171 func(ms *core.MetricSet) bool { return true }, 172 func(key string, ms *core.MetricSet) string { return key }) 173 } 174 175 func (this *MetricSink) GetNodes() []string { 176 return this.getAllNames( 177 func(ms *core.MetricSet) bool { return ms.Labels[core.LabelMetricSetType.Key] == core.MetricSetTypeNode }, 178 func(key string, ms *core.MetricSet) string { return ms.Labels[core.LabelHostname.Key] }) 179 } 180 181 func (this *MetricSink) GetPods() []string { 182 return this.getAllNames( 183 func(ms *core.MetricSet) bool { return ms.Labels[core.LabelMetricSetType.Key] == core.MetricSetTypePod }, 184 func(key string, ms *core.MetricSet) string { 185 return ms.Labels[core.LabelNamespaceName.Key] + "/" + ms.Labels[core.LabelPodName.Key] 186 }) 187 } 188 189 func (this *MetricSink) GetNamespaces() []string { 190 return this.getAllNames( 191 func(ms *core.MetricSet) bool { 192 return ms.Labels[core.LabelMetricSetType.Key] == core.MetricSetTypeNamespace 193 }, 194 func(key string, ms *core.MetricSet) string { return ms.Labels[core.LabelNamespaceName.Key] }) 195 } 196 197 func (this *MetricSink) GetPodsFromNamespace(namespace string) []string { 198 return this.getAllNames( 199 func(ms *core.MetricSet) bool { 200 return ms.Labels[core.LabelMetricSetType.Key] == core.MetricSetTypePod && 201 ms.Labels[core.LabelNamespaceName.Key] == namespace 202 }, 203 func(key string, ms *core.MetricSet) string { 204 return ms.Labels[core.LabelPodName.Key] 205 }) 206 } 207 208 func (this *MetricSink) GetSystemContainersFromNode(node string) []string { 209 return this.getAllNames( 210 func(ms *core.MetricSet) bool { 211 return ms.Labels[core.LabelMetricSetType.Key] == core.MetricSetTypeSystemContainer && 212 ms.Labels[core.LabelHostname.Key] == node 213 }, 214 func(key string, ms *core.MetricSet) string { 215 return ms.Labels[core.LabelContainerName.Key] 216 }) 217 } 218 219 func (this *MetricSink) trimDataBatch(batch *core.DataBatch) *core.DataBatch { 220 result := core.DataBatch{ 221 Timestamp: batch.Timestamp, 222 MetricSets: make(map[string]*core.MetricSet), 223 } 224 for metricSetKey, metricSet := range batch.MetricSets { 225 trimmedMetricSet := core.MetricSet{ 226 MetricValues: make(map[string]core.MetricValue), 227 } 228 for _, metricName := range this.longStoreMetrics { 229 metricValue, found := metricSet.MetricValues[metricName] 230 if found { 231 trimmedMetricSet.MetricValues[metricName] = metricValue 232 } 233 } 234 if len(trimmedMetricSet.MetricValues) > 0 { 235 result.MetricSets[metricSetKey] = &trimmedMetricSet 236 } 237 } 238 return &result 239 } 240 241 func popOld(storage []*core.DataBatch, cutoffTime time.Time) []*core.DataBatch { 242 result := make([]*core.DataBatch, 0) 243 for _, batch := range storage { 244 if batch.Timestamp.After(cutoffTime) { 245 result = append(result, batch) 246 } 247 } 248 return result 249 } 250 251 func NewMetricSink(shortStoreDuration, longStoreDuration time.Duration, longStoreMetrics []string) *MetricSink { 252 return &MetricSink{ 253 longStoreMetrics: longStoreMetrics, 254 longStoreDuration: longStoreDuration, 255 shortStoreDuration: shortStoreDuration, 256 longStore: make([]*core.DataBatch, 0), 257 shortStore: make([]*core.DataBatch, 0), 258 } 259 }