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  }