github.com/galamsiva2020/kubernetes-heapster-monitoring@v0.0.0-20210823134957-3c1baa7c1e70/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 metric 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 upfront. 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 func buildMultimetricStore(metrics []string, batch *core.DataBatch) *multimetricStore { 55 store := multimetricStore{ 56 timestamp: batch.Timestamp, 57 store: make(map[string]int64Store, len(metrics)), 58 } 59 for _, metric := range metrics { 60 store.store[metric] = make(int64Store, len(batch.MetricSets)) 61 } 62 for key, ms := range batch.MetricSets { 63 for _, metric := range metrics { 64 if metricValue, found := ms.MetricValues[metric]; found { 65 metricstore := store.store[metric] 66 metricstore[key] = metricValue.IntValue 67 } 68 } 69 } 70 return &store 71 } 72 73 func (this *MetricSink) Name() string { 74 return "Metric Sink" 75 } 76 77 func (this *MetricSink) Stop() { 78 // Do nothing. 79 } 80 81 func (this *MetricSink) ExportData(batch *core.DataBatch) { 82 this.lock.Lock() 83 defer this.lock.Unlock() 84 85 now := time.Now() 86 // TODO: add sorting 87 this.longStore = append(popOldStore(this.longStore, now.Add(-this.longStoreDuration)), 88 buildMultimetricStore(this.longStoreMetrics, batch)) 89 this.shortStore = append(popOld(this.shortStore, now.Add(-this.shortStoreDuration)), batch) 90 } 91 92 func (this *MetricSink) GetLatestDataBatch() *core.DataBatch { 93 this.lock.Lock() 94 defer this.lock.Unlock() 95 96 if len(this.shortStore) == 0 { 97 return nil 98 } 99 return this.shortStore[len(this.shortStore)-1] 100 } 101 102 func (this *MetricSink) GetShortStore() []*core.DataBatch { 103 this.lock.Lock() 104 defer this.lock.Unlock() 105 106 result := make([]*core.DataBatch, 0, len(this.shortStore)) 107 for _, batch := range this.shortStore { 108 result = append(result, batch) 109 } 110 return result 111 } 112 113 func (this *MetricSink) GetMetric(metricName string, keys []string, start, end time.Time) map[string][]core.TimestampedMetricValue { 114 this.lock.Lock() 115 defer this.lock.Unlock() 116 117 useLongStore := false 118 for _, longStoreMetric := range this.longStoreMetrics { 119 if longStoreMetric == metricName { 120 useLongStore = true 121 } 122 } 123 124 result := make(map[string][]core.TimestampedMetricValue) 125 if useLongStore { 126 for _, store := range this.longStore { 127 // Inclusive start and end. 128 if !store.timestamp.Before(start) && !store.timestamp.After(end) { 129 substore := store.store[metricName] 130 for _, key := range keys { 131 if val, found := substore[key]; found { 132 result[key] = append(result[key], core.TimestampedMetricValue{ 133 Timestamp: store.timestamp, 134 MetricValue: core.MetricValue{ 135 IntValue: val, 136 ValueType: core.ValueInt64, 137 MetricType: core.MetricGauge, 138 }, 139 }) 140 } 141 } 142 } 143 } 144 } else { 145 for _, batch := range this.shortStore { 146 // Inclusive start and end. 147 if !batch.Timestamp.Before(start) && !batch.Timestamp.After(end) { 148 for _, key := range keys { 149 metricSet, found := batch.MetricSets[key] 150 if !found { 151 continue 152 } 153 metricValue, found := metricSet.MetricValues[metricName] 154 if !found { 155 continue 156 } 157 keyResult, found := result[key] 158 if !found { 159 keyResult = make([]core.TimestampedMetricValue, 0) 160 } 161 keyResult = append(keyResult, core.TimestampedMetricValue{ 162 Timestamp: batch.Timestamp, 163 MetricValue: metricValue, 164 }) 165 result[key] = keyResult 166 } 167 } 168 } 169 } 170 return result 171 } 172 173 func (this *MetricSink) GetLabeledMetric(metricName string, labels map[string]string, keys []string, start, end time.Time) map[string][]core.TimestampedMetricValue { 174 // NB: the long store doesn't store labeled metrics, so it's not relevant here 175 result := make(map[string][]core.TimestampedMetricValue) 176 for _, batch := range this.shortStore { 177 // Inclusive start and end 178 if !batch.Timestamp.Before(start) && !batch.Timestamp.After(end) { 179 for _, key := range keys { 180 metricSet, found := batch.MetricSets[key] 181 if !found { 182 continue 183 } 184 185 for _, labeledMetric := range metricSet.LabeledMetrics { 186 if labeledMetric.Name != metricName { 187 continue 188 } 189 190 if len(labeledMetric.Labels) != len(labels) { 191 continue 192 } 193 194 labelsMatch := true 195 for k, v := range labels { 196 if lblMetricVal, ok := labeledMetric.Labels[k]; !ok || lblMetricVal != v { 197 labelsMatch = false 198 break 199 } 200 } 201 202 if labelsMatch { 203 result[key] = append(result[key], core.TimestampedMetricValue{ 204 Timestamp: batch.Timestamp, 205 MetricValue: labeledMetric.MetricValue, 206 }) 207 } 208 } 209 } 210 } 211 } 212 213 return result 214 } 215 216 func (this *MetricSink) GetMetricNames(key string) []string { 217 this.lock.Lock() 218 defer this.lock.Unlock() 219 220 metricNames := make(map[string]bool) 221 for _, batch := range this.shortStore { 222 if set, found := batch.MetricSets[key]; found { 223 for key := range set.MetricValues { 224 metricNames[key] = true 225 } 226 } 227 } 228 result := make([]string, 0, len(metricNames)) 229 for key := range metricNames { 230 result = append(result, key) 231 } 232 return result 233 } 234 235 func (this *MetricSink) getAllNames(predicate func(ms *core.MetricSet) bool, 236 name func(key string, ms *core.MetricSet) string) []string { 237 this.lock.Lock() 238 defer this.lock.Unlock() 239 240 if len(this.shortStore) == 0 { 241 return []string{} 242 } 243 244 result := make([]string, 0, 0) 245 for key, value := range this.shortStore[len(this.shortStore)-1].MetricSets { 246 if predicate(value) { 247 result = append(result, name(key, value)) 248 } 249 } 250 return result 251 } 252 253 /* 254 * For debugging only. 255 */ 256 func (this *MetricSink) GetMetricSetKeys() []string { 257 return this.getAllNames( 258 func(ms *core.MetricSet) bool { return true }, 259 func(key string, ms *core.MetricSet) string { return key }) 260 } 261 262 func (this *MetricSink) GetNodes() []string { 263 return this.getAllNames( 264 func(ms *core.MetricSet) bool { return ms.Labels[core.LabelMetricSetType.Key] == core.MetricSetTypeNode }, 265 func(key string, ms *core.MetricSet) string { return ms.Labels[core.LabelHostname.Key] }) 266 } 267 268 func (this *MetricSink) GetPods() []string { 269 return this.getAllNames( 270 func(ms *core.MetricSet) bool { return ms.Labels[core.LabelMetricSetType.Key] == core.MetricSetTypePod }, 271 func(key string, ms *core.MetricSet) string { 272 return ms.Labels[core.LabelNamespaceName.Key] + "/" + ms.Labels[core.LabelPodName.Key] 273 }) 274 } 275 276 func (this *MetricSink) GetNamespaces() []string { 277 return this.getAllNames( 278 func(ms *core.MetricSet) bool { 279 return ms.Labels[core.LabelMetricSetType.Key] == core.MetricSetTypeNamespace 280 }, 281 func(key string, ms *core.MetricSet) string { return ms.Labels[core.LabelNamespaceName.Key] }) 282 } 283 284 func (this *MetricSink) GetPodsFromNamespace(namespace string) []string { 285 return this.getAllNames( 286 func(ms *core.MetricSet) bool { 287 return ms.Labels[core.LabelMetricSetType.Key] == core.MetricSetTypePod && 288 ms.Labels[core.LabelNamespaceName.Key] == namespace 289 }, 290 func(key string, ms *core.MetricSet) string { 291 return ms.Labels[core.LabelPodName.Key] 292 }) 293 } 294 295 func (this *MetricSink) GetContainersForPodFromNamespace(namespace, pod string) []string { 296 return this.getAllNames( 297 func(ms *core.MetricSet) bool { 298 return ms.Labels[core.LabelMetricSetType.Key] == core.MetricSetTypePodContainer && 299 ms.Labels[core.LabelNamespaceName.Key] == namespace && 300 ms.Labels[core.LabelPodName.Key] == pod 301 }, 302 func(key string, ms *core.MetricSet) string { 303 return ms.Labels[core.LabelContainerName.Key] 304 }) 305 } 306 307 func (this *MetricSink) GetSystemContainersFromNode(node string) []string { 308 return this.getAllNames( 309 func(ms *core.MetricSet) bool { 310 return ms.Labels[core.LabelMetricSetType.Key] == core.MetricSetTypeSystemContainer && 311 ms.Labels[core.LabelHostname.Key] == node 312 }, 313 func(key string, ms *core.MetricSet) string { 314 return ms.Labels[core.LabelContainerName.Key] 315 }) 316 } 317 318 func popOld(storage []*core.DataBatch, cutoffTime time.Time) []*core.DataBatch { 319 result := make([]*core.DataBatch, 0) 320 for _, batch := range storage { 321 if batch.Timestamp.After(cutoffTime) { 322 result = append(result, batch) 323 } 324 } 325 return result 326 } 327 328 func popOldStore(storages []*multimetricStore, cutoffTime time.Time) []*multimetricStore { 329 result := make([]*multimetricStore, 0, len(storages)) 330 for _, store := range storages { 331 if store.timestamp.After(cutoffTime) { 332 result = append(result, store) 333 } 334 } 335 return result 336 } 337 338 func NewMetricSink(shortStoreDuration, longStoreDuration time.Duration, longStoreMetrics []string) *MetricSink { 339 return &MetricSink{ 340 longStoreMetrics: longStoreMetrics, 341 longStoreDuration: longStoreDuration, 342 shortStoreDuration: shortStoreDuration, 343 longStore: make([]*multimetricStore, 0), 344 shortStore: make([]*core.DataBatch, 0), 345 } 346 }