diff --git a/.gitignore b/.gitignore index bb02b6e..f60aa80 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ gin-bin mondash Godeps/_workspace/ +*.sh diff --git a/main.go b/main.go index b8a4a87..6e68d7f 100644 --- a/main.go +++ b/main.go @@ -5,13 +5,13 @@ import ( "fmt" "io/ioutil" "net/http" - "os" + //"os" "time" - "log" + //"log" - "launchpad.net/goamz/aws" - "launchpad.net/goamz/s3" + //"launchpad.net/goamz/aws" + //"launchpad.net/goamz/s3" "github.com/flosch/pongo2" "github.com/go-martini/martini" @@ -20,18 +20,19 @@ import ( ) var templates = make(map[string]*pongo2.Template) -var s3Storage *s3.Bucket + +//var s3Storage *s3.Bucket func main() { preloadTemplates() // Initialize S3 storage - awsAuth, err := aws.EnvAuth() - if err != nil { - log.Fatal(err) - } - s3Conn := s3.New(awsAuth, aws.EUWest) - s3Storage = s3Conn.Bucket(os.Getenv("S3Bucket")) + //awsAuth, err := aws.EnvAuth() + //if err != nil { + // log.Fatal(err) + //} + //s3Conn := s3.New(awsAuth, aws.USEast) + //s3Storage = s3Conn.Bucket(os.Getenv("S3Bucket")) m := martini.Classic() diff --git a/structs.go b/structs.go index 8327a6d..ff2c417 100644 --- a/structs.go +++ b/structs.go @@ -3,10 +3,13 @@ package main import ( "encoding/json" "errors" + "io/ioutil" + //"launchpad.net/goamz/s3" "log" + //"os" + "sort" + "strconv" "time" - - "launchpad.net/goamz/s3" ) type dashboard struct { @@ -16,7 +19,7 @@ type dashboard struct { } func loadDashboard(dashid string) (*dashboard, error) { - data, err := s3Storage.Get(dashid) + data, err := ioutil.ReadFile(dashid + ".txt") if err != nil { return &dashboard{}, errors.New("Dashboard not found") } @@ -33,7 +36,8 @@ func (d *dashboard) Save() { log.Printf("Error while marshalling dashboard: %s", err) return } - err = s3Storage.Put(d.DashboardID, data, "application/json", s3.Private) + err = ioutil.WriteFile(d.DashboardID+".txt", data, 0600) + if err != nil { log.Printf("Error while storing dashboard: %s", err) } @@ -52,6 +56,7 @@ type dashboardMetric struct { Title string `json:"title"` Description string `json:"description"` Status string `json:"status"` + Value float64 `json:"value,omitifempty"` Expires int64 `json:"expires,omitifempty"` Freshness int64 `json:"freshness,omitifempty"` HistoricalData dashboardMetricHistory `json:"history,omitifempty"` @@ -61,6 +66,7 @@ type dashboardMetric struct { type dashboardMetricStatus struct { Time time.Time `json:"time"` Status string `json:"status"` + Value float64 `json:"value"` } type dashboardMetricMeta struct { @@ -83,20 +89,121 @@ func newDashboardMetric() *dashboardMetric { } } +func median(values []float64) float64 { + sort.Float64s(values) + + if len(values) == 1 { + return values[0] + } + + // If even, take an average + if len(values)%2 == 0 { + return 0.5*values[len(values)/2] + 0.5*values[len(values)/2-1] + } + + log.Printf("len(values)=%v, len(values)/2=%v\n", len(values), len(values)/2) + + return values[len(values)/2-1] +} + +func absoluteValue(value float64) float64 { + if value < 0 { + value = -value + } + return value +} + +func absoluteDeviation(values []float64) []float64 { + medianValue := median(values) + + deviation := make([]float64, len(values)) + + for i, _ := range values { + deviation[i] = absoluteValue(values[i] - medianValue) + } + + return deviation +} + +func (dm *dashboardMetric) getValueArray() []float64 { + values := make([]float64, 0) + + for _, v := range dm.HistoricalData { + values = append(values, v.Value) + } + + return values +} + +func (dm *dashboardMetric) Median() float64 { + return median(dm.getValueArray()) +} + +func (dm *dashboardMetric) MedianAbsoluteDeviation() (float64, float64) { + values := dm.getValueArray() + medianValue := dm.Median() + + return medianValue, median(absoluteDeviation(values)) +} + +func (dm *dashboardMetric) MadMultiplier() float64 { + medianValue, MAD := dm.MedianAbsoluteDeviation() + + return absoluteValue(dm.Value-medianValue) / MAD +} + +func (dm *dashboardMetric) StatisticalStatus() string { + mult := dm.MadMultiplier() + + if mult > 4 { + return "Critical" + } else if mult > 3 { + return "Warning" + } + + return "OK" +} + +func (dm *dashboardMetric) LabelHistory() string { + s := "[" + for i, v := range dm.HistoricalData { + if i != 0 { + s = s + ", " + } + s = s + "" + strconv.Itoa(int(v.Time.Unix())) + "" + } + s = s + "]" + return s +} + +func (dm *dashboardMetric) DataHistory() string { + s := "[" + for i, v := range dm.HistoricalData { + if i != 0 { + s = s + ", " + } + s = s + strconv.FormatFloat(v.Value, 'g', 4, 64) + } + s = s + "]" + return s +} + func (dm *dashboardMetric) Update(m *dashboardMetric) { dm.Title = m.Title dm.Description = m.Description dm.Status = m.Status + dm.Value = m.Value if m.Expires != 0 { dm.Expires = m.Expires } if m.Freshness != 0 { dm.Freshness = m.Freshness } - dm.HistoricalData = append(dashboardMetricHistory{dashboardMetricStatus{ + dm.HistoricalData = append(dm.HistoricalData, dashboardMetricStatus{ Time: time.Now(), Status: m.Status, - }}, dm.HistoricalData...) + Value: m.Value, + }) countStatus := make(map[string]float64) diff --git a/templates/dashboard.html b/templates/dashboard.html index 4ab3952..50c84a4 100644 --- a/templates/dashboard.html +++ b/templates/dashboard.html @@ -10,6 +10,24 @@ + + + + +