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  }