1
0
mirror of https://github.com/Luzifer/mondash.git synced 2024-09-20 01:12:58 +00:00

Merge remote-tracking branch 'alphahat/master'

This commit is contained in:
Knut Ahlers 2015-07-06 20:27:41 +02:00
commit 55386a5eab
5 changed files with 168 additions and 23 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
gin-bin gin-bin
mondash mondash
Godeps/_workspace/ Godeps/_workspace/
*.sh

23
main.go
View File

@ -5,13 +5,13 @@ import (
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"os" //"os"
"time" "time"
"log" //"log"
"launchpad.net/goamz/aws" //"launchpad.net/goamz/aws"
"launchpad.net/goamz/s3" //"launchpad.net/goamz/s3"
"github.com/flosch/pongo2" "github.com/flosch/pongo2"
"github.com/go-martini/martini" "github.com/go-martini/martini"
@ -20,18 +20,19 @@ import (
) )
var templates = make(map[string]*pongo2.Template) var templates = make(map[string]*pongo2.Template)
var s3Storage *s3.Bucket
//var s3Storage *s3.Bucket
func main() { func main() {
preloadTemplates() preloadTemplates()
// Initialize S3 storage // Initialize S3 storage
awsAuth, err := aws.EnvAuth() //awsAuth, err := aws.EnvAuth()
if err != nil { //if err != nil {
log.Fatal(err) // log.Fatal(err)
} //}
s3Conn := s3.New(awsAuth, aws.EUWest) //s3Conn := s3.New(awsAuth, aws.USEast)
s3Storage = s3Conn.Bucket(os.Getenv("S3Bucket")) //s3Storage = s3Conn.Bucket(os.Getenv("S3Bucket"))
m := martini.Classic() m := martini.Classic()

View File

@ -3,10 +3,13 @@ package main
import ( import (
"encoding/json" "encoding/json"
"errors" "errors"
"io/ioutil"
//"launchpad.net/goamz/s3"
"log" "log"
//"os"
"sort"
"strconv"
"time" "time"
"launchpad.net/goamz/s3"
) )
type dashboard struct { type dashboard struct {
@ -16,7 +19,7 @@ type dashboard struct {
} }
func loadDashboard(dashid string) (*dashboard, error) { func loadDashboard(dashid string) (*dashboard, error) {
data, err := s3Storage.Get(dashid) data, err := ioutil.ReadFile(dashid + ".txt")
if err != nil { if err != nil {
return &dashboard{}, errors.New("Dashboard not found") return &dashboard{}, errors.New("Dashboard not found")
} }
@ -33,7 +36,8 @@ func (d *dashboard) Save() {
log.Printf("Error while marshalling dashboard: %s", err) log.Printf("Error while marshalling dashboard: %s", err)
return return
} }
err = s3Storage.Put(d.DashboardID, data, "application/json", s3.Private) err = ioutil.WriteFile(d.DashboardID+".txt", data, 0600)
if err != nil { if err != nil {
log.Printf("Error while storing dashboard: %s", err) log.Printf("Error while storing dashboard: %s", err)
} }
@ -52,6 +56,7 @@ type dashboardMetric struct {
Title string `json:"title"` Title string `json:"title"`
Description string `json:"description"` Description string `json:"description"`
Status string `json:"status"` Status string `json:"status"`
Value float64 `json:"value,omitifempty"`
Expires int64 `json:"expires,omitifempty"` Expires int64 `json:"expires,omitifempty"`
Freshness int64 `json:"freshness,omitifempty"` Freshness int64 `json:"freshness,omitifempty"`
HistoricalData dashboardMetricHistory `json:"history,omitifempty"` HistoricalData dashboardMetricHistory `json:"history,omitifempty"`
@ -61,6 +66,7 @@ type dashboardMetric struct {
type dashboardMetricStatus struct { type dashboardMetricStatus struct {
Time time.Time `json:"time"` Time time.Time `json:"time"`
Status string `json:"status"` Status string `json:"status"`
Value float64 `json:"value"`
} }
type dashboardMetricMeta struct { 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) { func (dm *dashboardMetric) Update(m *dashboardMetric) {
dm.Title = m.Title dm.Title = m.Title
dm.Description = m.Description dm.Description = m.Description
dm.Status = m.Status dm.Status = m.Status
dm.Value = m.Value
if m.Expires != 0 { if m.Expires != 0 {
dm.Expires = m.Expires dm.Expires = m.Expires
} }
if m.Freshness != 0 { if m.Freshness != 0 {
dm.Freshness = m.Freshness dm.Freshness = m.Freshness
} }
dm.HistoricalData = append(dashboardMetricHistory{dashboardMetricStatus{ dm.HistoricalData = append(dm.HistoricalData, dashboardMetricStatus{
Time: time.Now(), Time: time.Now(),
Status: m.Status, Status: m.Status,
}}, dm.HistoricalData...) Value: m.Value,
})
countStatus := make(map[string]float64) countStatus := make(map[string]float64)

View File

@ -10,6 +10,24 @@
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap-theme.min.css"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap-theme.min.css">
<!-- Chartist Library -->
<link rel="stylesheet" href="//cdn.jsdelivr.net/chartist.js/latest/chartist.min.css">
<script src="//cdn.jsdelivr.net/chartist.js/latest/chartist.min.js"></script>
<script>
function timeConverter(UNIX_timestamp){
var a = new Date(UNIX_timestamp*1000);
var months = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
var year = a.getFullYear();
var month = months[a.getMonth()];
var date = a.getDate();
var hour = a.getHours();
var min = a.getMinutes();
var sec = a.getSeconds();
var time = date + ' ' + month + ' ' + year + ' ' + hour + ':' + min + ':' + sec ;
return time;
}
</script>
<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries --> <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// --> <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]> <!--[if lt IE 9]>
@ -63,11 +81,11 @@
</div> </div>
{% else %} {% else %}
{% for metric in metrics %} {% for metric in metrics %}
{% if metric.Status == "OK" %} {% if metric.StatisticalStatus == "OK" %}
<div class="row alert alert-success"> <div class="row alert alert-success">
{% elif metric.Status == "Warning" %} {% elif metric.StatisticalStatus == "Warning" %}
<div class="row alert alert-warning"> <div class="row alert alert-warning">
{% elif metric.Status == "Critical" %} {% elif metric.StatisticalStatus == "Critical" %}
<div class="row alert alert-danger"> <div class="row alert alert-danger">
{% else %} {% else %}
<div class="row alert alert-info"> <div class="row alert alert-info">
@ -75,6 +93,24 @@
<div class="col-md-9"> <div class="col-md-9">
<h4>{{ metric.Title }}</h4> <h4>{{ metric.Title }}</h4>
<p>{{ metric.Description }}</p> <p>{{ metric.Description }}</p>
<span><span class="label label-info">Current Value: {{ metric.Value }}</span> {{ metric.MadMultiplier }} MAD above the Median ({{ metric.Median }})</span>
<div class="ct-chart {{metric.MetricID}} .ct-double-octave"></div>
<script>
var data = {
// A labels array that can contain any sort of values
labels: {{ metric.LabelHistory }},
// Our series array that contains series objects or in this case series data arrays
series: [
{{ metric.DataHistory }}
]
};
for (var i = 0; i < data.labels.length; i++) {
data.labels[i] = timeConverter(data.labels[i]);
}
new Chartist.Line('.{{metric.MetricID}}', data);
</script>
<small> <small>
Updated {{ metric.Meta.LastUpdate|naturaltime}} Updated {{ metric.Meta.LastUpdate|naturaltime}}
{% if metric.Status != "OK" %} {% if metric.Status != "OK" %}

View File

@ -19,10 +19,10 @@ func handleRedirectWelcome(res http.ResponseWriter, req *http.Request) {
func handleCreateRandomDashboard(res http.ResponseWriter, req *http.Request) { func handleCreateRandomDashboard(res http.ResponseWriter, req *http.Request) {
urlProposal := generateAPIKey()[0:20] urlProposal := generateAPIKey()[0:20]
_, err := s3Storage.Get(urlProposal) _, err := ioutil.ReadFile(urlProposal + ".txt")
for err == nil { for err == nil {
urlProposal = generateAPIKey()[0:20] urlProposal = generateAPIKey()[0:20]
_, err = s3Storage.Get(urlProposal) _, err = ioutil.ReadFile(urlProposal + ".txt")
} }
http.Redirect(res, req, fmt.Sprintf("/%s", urlProposal), http.StatusTemporaryRedirect) http.Redirect(res, req, fmt.Sprintf("/%s", urlProposal), http.StatusTemporaryRedirect)
} }
@ -62,7 +62,7 @@ func handleDeleteDashboard(params martini.Params, req *http.Request, res http.Re
return return
} }
_ = s3Storage.Del(params["dashid"]) //_ = s3Storage.Del(params["dashid"])
http.Error(res, "OK", http.StatusOK) http.Error(res, "OK", http.StatusOK)
} }