bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/cmd/scollector/collectors/interval.go (about) 1 package collectors 2 3 import ( 4 "io" 5 "io/ioutil" 6 "net/http" 7 "os/exec" 8 "reflect" 9 "regexp" 10 "runtime" 11 "sync" 12 "time" 13 14 "bosun.org/collect" 15 "bosun.org/metadata" 16 "bosun.org/opentsdb" 17 "bosun.org/slog" 18 ) 19 20 type IntervalCollector struct { 21 F func() (opentsdb.MultiDataPoint, error) 22 Interval time.Duration // defaults to DefaultFreq if unspecified 23 Enable func() bool 24 name string 25 init func() 26 27 // internal use 28 sync.Mutex 29 enabled bool 30 31 TagOverride 32 } 33 34 func (c *IntervalCollector) Init() { 35 if c.init != nil { 36 c.init() 37 } 38 } 39 40 func (c *IntervalCollector) Run(dpchan chan<- *opentsdb.DataPoint, quit <-chan struct{}) { 41 if c.Enable != nil { 42 go func() { 43 for { 44 next := time.After(time.Minute * 5) 45 c.Lock() 46 c.enabled = c.Enable() 47 c.Unlock() 48 <-next 49 } 50 }() 51 } 52 53 for { 54 interval := c.Interval 55 if interval == 0 { 56 interval = DefaultFreq 57 } 58 next := time.After(interval) 59 if c.Enabled() { 60 timeStart := time.Now() 61 md, err := c.F() 62 timeFinish := time.Since(timeStart) 63 result := 0 64 if err != nil { 65 slog.Errorf("%v: %v", c.Name(), err) 66 result = 1 67 } 68 if !collect.DisableDefaultCollectors { 69 tags := opentsdb.TagSet{"collector": c.Name(), "os": runtime.GOOS} 70 Add(&md, "scollector.collector.duration", timeFinish.Seconds(), tags, metadata.Gauge, metadata.Second, "Duration in seconds for each collector run.") 71 Add(&md, "scollector.collector.error", result, tags, metadata.Gauge, metadata.Ok, "Status of collector run. 1=Error, 0=Success.") 72 } 73 for _, dp := range md { 74 c.ApplyTagOverrides(dp.Tags) 75 dpchan <- dp 76 } 77 } 78 select { 79 case <-next: 80 case <-quit: 81 return 82 } 83 84 } 85 } 86 87 func (c *IntervalCollector) Enabled() bool { 88 if c.Enable == nil { 89 return true 90 } 91 c.Lock() 92 defer c.Unlock() 93 return c.enabled 94 } 95 96 func (c *IntervalCollector) Name() string { 97 if c.name != "" { 98 return c.name 99 } 100 v := runtime.FuncForPC(reflect.ValueOf(c.F).Pointer()) 101 return v.Name() 102 } 103 104 func enableURL(url string, regexes ...string) func() bool { 105 res := make([]*regexp.Regexp, len(regexes)) 106 for i, r := range regexes { 107 res[i] = regexp.MustCompile(r) 108 } 109 return func() bool { 110 resp, err := http.Get(url) 111 if err != nil { 112 return false 113 } 114 defer func() { 115 // Drain up to 512 bytes and close the body to let the Transport reuse the connection 116 io.CopyN(ioutil.Discard, resp.Body, 512) 117 resp.Body.Close() 118 }() 119 if resp.StatusCode != 200 { 120 slog.Infof("URL not enabled: %v; HTTP Response not 200, but %v", url, resp.StatusCode) 121 return false 122 } 123 if len(res) == 0 { 124 return true 125 } 126 b, err := ioutil.ReadAll(resp.Body) 127 if err != nil { 128 return false 129 } 130 for _, r := range res { 131 if !r.Match(b) { 132 return false 133 } 134 } 135 return true 136 } 137 } 138 139 // enableExecutable returns true if name is an executable file in the 140 // environment's PATH. 141 func enableExecutable(name string) func() bool { 142 return func() bool { 143 _, err := exec.LookPath(name) 144 return err == nil 145 } 146 }