github.com/google/cloudprober@v0.11.3/probes/common/statskeeper/statskeeper.go (about)

     1  // Copyright 2019 The Cloudprober Authors.
     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  /*
    16  Package statskeeper implements utilities that are shared across multiple probe
    17  types.
    18  */
    19  package statskeeper
    20  
    21  import (
    22  	"context"
    23  	"time"
    24  
    25  	"github.com/google/cloudprober/metrics"
    26  	"github.com/google/cloudprober/probes/options"
    27  	"github.com/google/cloudprober/targets/endpoint"
    28  )
    29  
    30  // ProbeResult represents results of a probe run.
    31  type ProbeResult interface {
    32  	// Metrics converts ProbeResult into a map of the metrics that is suitable for
    33  	// working with metrics.EventMetrics.
    34  	Metrics() *metrics.EventMetrics
    35  
    36  	// Target returns the target associated with the probe result.
    37  	Target() string
    38  }
    39  
    40  // StatsKeeper manages and outputs probe results.
    41  //
    42  // Typical StatsKeeper usage pattern is that the probes start a StatsKeeper
    43  // goroutine in the beginning. StatsKeeper goroutine manages access to the
    44  // per-target cumulative metrics. It listens on an input channel for probe
    45  // results and updates the metrics whenever a new probe result is obtained.
    46  // It exports aggregate probe statistics to the output channel, at intervals
    47  // controlled by a Ticker. These two operations are mutually exclusive. This
    48  // is the only goroutine that accesses the metrics. StatsKeeper runs
    49  // indefinitely, across multiple probe runs, and should not stop during normal
    50  // program execution.
    51  //
    52  // If we get a new result on resultsChan, update the probe statistics.
    53  // If we get a timer tick on doExport, export probe data for all targets.
    54  // If context is canceled, return.
    55  //
    56  // Note that StatsKeeper calls a function (targetsFunc) to get the list of the
    57  // targets for exporting results,  instead of getting a static list in the
    58  // arguments. We do that as the list of targets is usually dynamic and is
    59  // updated on a regular basis.
    60  func StatsKeeper(ctx context.Context, ptype, name string, opts *options.Options, targetsFunc func() []endpoint.Endpoint, resultsChan <-chan ProbeResult, dataChan chan<- *metrics.EventMetrics) {
    61  	targetMetrics := make(map[string]*metrics.EventMetrics)
    62  	exportTicker := time.NewTicker(opts.StatsExportInterval)
    63  	defer exportTicker.Stop()
    64  
    65  	for {
    66  		select {
    67  		case result := <-resultsChan:
    68  			// result is a ProbeResult
    69  			t := result.Target()
    70  			if targetMetrics[t] == nil {
    71  				targetMetrics[t] = result.Metrics()
    72  				continue
    73  			}
    74  			err := targetMetrics[t].Update(result.Metrics())
    75  			if err != nil {
    76  				opts.Logger.Errorf("Error adding metrics from the probe result for the target: %s. Err: %v", t, err)
    77  			}
    78  		case ts := <-exportTicker.C:
    79  			for _, t := range targetsFunc() {
    80  				em := targetMetrics[t.Name]
    81  				if em != nil {
    82  					em.AddLabel("ptype", ptype)
    83  					em.AddLabel("probe", name)
    84  					em.AddLabel("dst", t.Name)
    85  					em.Timestamp = ts
    86  
    87  					em.LatencyUnit = opts.LatencyUnit
    88  
    89  					for _, al := range opts.AdditionalLabels {
    90  						em.AddLabel(al.KeyValueForTarget(t.Name))
    91  					}
    92  
    93  					if opts.LogMetrics != nil {
    94  						opts.LogMetrics(em)
    95  					}
    96  					dataChan <- em.Clone()
    97  				}
    98  			}
    99  		case <-ctx.Done():
   100  			return
   101  		}
   102  	}
   103  }