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  }