Skip to content
Snippets Groups Projects
Select Git revision
  • main default protected
  • v3.0
  • v3-alpha-1
  • v2.0
  • v1.1.0
  • v1.0.0
6 results

monitor.go

Blame
  • monitor.go 2.96 KiB
    package main
    
    import (
    	"github.com/sirupsen/logrus"
    	"strconv"
    	"sync"
    	"time"
    )
    
    type Datapoint struct {
    	Time    time.Time
    	Up      bool
    	Latency int
    }
    
    type MonitorWrapper struct {
    	api     *ConfigApi
    	storage *Storage
    
    	monitor Monitor
    	config  *ConfigComponent
    
    	thresholdDuration time.Duration
    
    	// Closed when mon.Stop() is called
    	stopC chan bool
    }
    
    func NewMonitorWrapper(api *ConfigApi, storage *Storage, monitor Monitor) MonitorWrapper {
    	config := monitor.Config()
    
    	threshold, err := time.ParseDuration(config.ThresholdDuration)
    	if err != nil {
    		panic(err)
    	}
    
    	return MonitorWrapper{
    		api:     api,
    		storage: storage,
    
    		monitor: monitor,
    		config:  config,
    
    		thresholdDuration: threshold,
    	}
    }
    func (w *MonitorWrapper) Name() string {
    	return w.config.Name
    }
    
    func (w *MonitorWrapper) ClockStart(immediate bool, wg *sync.WaitGroup) {
    	wg.Add(1)
    	w.stopC = make(chan bool)
    	if immediate {
    		w.tick()
    	}
    
    	ticker := time.NewTicker(Interval(w.config))
    	for {
    		select {
    		case <-ticker.C:
    			w.tick()
    		case <-w.stopC:
    			wg.Done()
    			return
    		}
    	}
    }
    
    func (w *MonitorWrapper) tick() {
    	datapoint, err := w.monitor.Measure()
    
    	if err != nil {
    		logrus.Errorf("Monitor %s encountered error: %s\n", w.config.Name, err.Error())
    		return
    	}
    
    	if w.config.ComponentId != 0 {
    		status := DetermineStatus(w.config, w.storage.Store(w.config.ComponentId, datapoint, w.thresholdDuration))
    		_, err = w.api.Components().Save(w.config.ComponentId, ApiComponentBody{
    			Status: status,
    		})
    		if err != nil {
    			logrus.Error(err.Error())
    		}
    	}
    	if w.config.MetricId != 0 {
    		_, err = w.api.MetricPoints(w.config.MetricId).Create(ApiMetricPointBody{
    			Metric:    w.config.MetricId,
    			Value:     datapoint.Latency,
    			Timestamp: strconv.FormatInt(datapoint.Time.Unix(), 10),
    		})
    		if err != nil {
    			logrus.Error(err.Error())
    		}
    	}
    }
    
    func (w *MonitorWrapper) ClockStop() {
    	select {
    	case <-w.stopC:
    		return
    	default:
    		close(w.stopC)
    	}
    }
    
    type Monitor interface {
    	Config() *ConfigComponent
    	Measure() (Datapoint, error)
    }
    
    func Interval(component *ConfigComponent) time.Duration {
    	interval, err := time.ParseDuration(component.Interval)
    	if err != nil {
    		panic(err)
    	}
    	return interval
    }
    
    func DetermineStatus(component *ConfigComponent, datapoints []Datapoint) int {
    	countSlow := 0
    	countDown := 0
    
    	for _, datapoint := range datapoints {
    		if !datapoint.Up {
    			countDown++
    		} else if datapoint.Latency >= component.LatencyThresholds.Down {
    			countDown++
    		} else if datapoint.Latency >= component.LatencyThresholds.Slow {
    			countSlow++
    		}
    	}
    
    	percentageSlow := countSlow * 100 / len(datapoints)
    	percentageDown := countDown * 100 / len(datapoints)
    
    	if percentageDown > component.OutageThresholds.Major {
    		return ComponentStatusMajorOutage
    	} else if percentageDown > component.OutageThresholds.Partial {
    		return ComponentStatusPartialOutage
    	} else if percentageSlow > component.OutageThresholds.Performance {
    		return ComponentStatusPerformanceIssues
    	} else {
    		return ComponentStatusOperational
    	}
    }