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 }