mirror of https://github.com/statping/statping
parent
b4ff76c70d
commit
b58326a453
|
@ -18,7 +18,7 @@ services:
|
||||||
|
|
||||||
env:
|
env:
|
||||||
global:
|
global:
|
||||||
- VERSION=0.28.91
|
- VERSION=0.29
|
||||||
- DB_HOST=localhost
|
- DB_HOST=localhost
|
||||||
- DB_USER=travis
|
- DB_USER=travis
|
||||||
- DB_PASS=
|
- DB_PASS=
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
FROM alpine:latest
|
FROM alpine:latest
|
||||||
|
|
||||||
ENV VERSION=v0.28.91
|
ENV VERSION=v0.29
|
||||||
|
|
||||||
RUN apk --no-cache add libstdc++ ca-certificates
|
RUN apk --no-cache add libstdc++ ca-certificates
|
||||||
RUN wget -q https://github.com/hunterlong/statup/releases/download/$VERSION/statup-linux-alpine.tar.gz && \
|
RUN wget -q https://github.com/hunterlong/statup/releases/download/$VERSION/statup-linux-alpine.tar.gz && \
|
||||||
|
|
|
@ -131,9 +131,9 @@ func (s *Service) GraphData() string {
|
||||||
// group by interval sql query for postgres, mysql and sqlite
|
// group by interval sql query for postgres, mysql and sqlite
|
||||||
sql := fmt.Sprintf("SELECT date_trunc('%v', created_at), AVG(latency)*1000 AS value FROM hits WHERE service=%v AND created_at > '%v' GROUP BY 1 ORDER BY date_trunc ASC;", increment, s.Id, since.Format(time.RFC3339))
|
sql := fmt.Sprintf("SELECT date_trunc('%v', created_at), AVG(latency)*1000 AS value FROM hits WHERE service=%v AND created_at > '%v' GROUP BY 1 ORDER BY date_trunc ASC;", increment, s.Id, since.Format(time.RFC3339))
|
||||||
if dbServer == "mysql" {
|
if dbServer == "mysql" {
|
||||||
sql = fmt.Sprintf("SELECT CONCAT(date_format(created_at, '%%Y-%%m-%%dT%%TZ')) AS created_at, AVG(latency)*1000 AS value FROM hits WHERE service=%v AND DATE_FORMAT(created_at, '%%Y-%%m-%%dT%%TZ') BETWEEN DATE_FORMAT(NOW() - INTERVAL 12 HOUR, '%%Y-%%m-%%dT%%TZ') AND DATE_FORMAT(NOW(), '%%Y-%%m-%%dT%%TZ') GROUP BY created_at", s.Id)
|
sql = fmt.Sprintf("SELECT CONCAT(date_format(created_at, '%%Y-%%m-%%dT%%TZ')) AS created_at, AVG(latency)*1000 AS value FROM hits WHERE service=%v AND DATE_FORMAT(created_at, '%%Y-%%m-%%dT%%TZ') BETWEEN DATE_FORMAT(NOW() - INTERVAL 12 HOUR, '%%Y-%%m-%%dT%%TZ') AND DATE_FORMAT(NOW(), '%%Y-%%m-%%dT%%TZ') GROUP BY created_at ORDER BY created_at ASC;", s.Id)
|
||||||
} else if dbServer == "sqlite" {
|
} else if dbServer == "sqlite" {
|
||||||
sql = fmt.Sprintf("SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%SZ', created_at), AVG(latency)*1000 as value FROM hits WHERE service=%v AND created_at >= '%v' GROUP BY strftime('%%M', created_at)", s.Id, since.Format(time.RFC3339))
|
sql = fmt.Sprintf("SELECT strftime('%%Y-%%m-%%dT%%H:%%M:%%SZ', created_at), AVG(latency)*1000 as value FROM hits WHERE service=%v AND created_at >= '%v' GROUP BY strftime('%%M', created_at) ORDER BY created_at ASC;", s.Id, since.Format(time.RFC3339))
|
||||||
}
|
}
|
||||||
dated, err := DbSession.Query(db.Raw(sql))
|
dated, err := DbSession.Query(db.Raw(sql))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -98,4 +98,19 @@ func ExecuteResponse(w http.ResponseWriter, r *http.Request, file string, data i
|
||||||
t.Execute(w, data)
|
t.Execute(w, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ExecuteJSResponse(w http.ResponseWriter, r *http.Request, file string, data interface{}) {
|
||||||
|
render, err := core.JsBox.String(file)
|
||||||
|
if err != nil {
|
||||||
|
utils.Log(4, err)
|
||||||
|
}
|
||||||
|
t := template.New("charts")
|
||||||
|
t.Funcs(template.FuncMap{
|
||||||
|
"safe": func(html string) template.HTML {
|
||||||
|
return template.HTML(html)
|
||||||
|
},
|
||||||
|
})
|
||||||
|
t.Parse(render)
|
||||||
|
t.Execute(w, data)
|
||||||
|
}
|
||||||
|
|
||||||
type DbConfig types.DbConfig
|
type DbConfig types.DbConfig
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/gorilla/sessions"
|
"github.com/gorilla/sessions"
|
||||||
"github.com/hunterlong/statup/core"
|
"github.com/hunterlong/statup/core"
|
||||||
|
"github.com/tdewolff/minify"
|
||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -11,6 +12,8 @@ func Router() *mux.Router {
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
r.Handle("/", http.HandlerFunc(IndexHandler))
|
r.Handle("/", http.HandlerFunc(IndexHandler))
|
||||||
LocalizedAssets(r)
|
LocalizedAssets(r)
|
||||||
|
m := minify.New()
|
||||||
|
r.Handle("/charts.js", m.Middleware(http.HandlerFunc(RenderServiceChartsHandler)))
|
||||||
r.Handle("/setup", http.HandlerFunc(SetupHandler)).Methods("GET")
|
r.Handle("/setup", http.HandlerFunc(SetupHandler)).Methods("GET")
|
||||||
r.Handle("/setup", http.HandlerFunc(ProcessSetupHandler)).Methods("POST")
|
r.Handle("/setup", http.HandlerFunc(ProcessSetupHandler)).Methods("POST")
|
||||||
r.Handle("/dashboard", http.HandlerFunc(DashboardHandler)).Methods("GET")
|
r.Handle("/dashboard", http.HandlerFunc(DashboardHandler)).Methods("GET")
|
||||||
|
|
|
@ -9,6 +9,13 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func RenderServiceChartsHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
services := core.CoreApp.Services
|
||||||
|
//w.Header().Set("Content-Type", "text/javascript")
|
||||||
|
//w.Header().Set("Cache-Control", "no-cache, private, max-age=0")
|
||||||
|
ExecuteJSResponse(w, r, "charts.js", services)
|
||||||
|
}
|
||||||
|
|
||||||
func ServicesHandler(w http.ResponseWriter, r *http.Request) {
|
func ServicesHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if !IsAuthenticated(r) {
|
if !IsAuthenticated(r) {
|
||||||
http.Redirect(w, r, "/", http.StatusSeeOther)
|
http.Redirect(w, r, "/", http.StatusSeeOther)
|
||||||
|
|
|
@ -0,0 +1,146 @@
|
||||||
|
{{ range . }}{{ if .AvgTime }}var ctx = document.getElementById("service_{{.Id}}").getContext('2d');
|
||||||
|
|
||||||
|
var chartdata = new Chart(ctx, {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
datasets: [{
|
||||||
|
label: 'Response Time (Milliseconds)',
|
||||||
|
data: {{safe .GraphData}},
|
||||||
|
backgroundColor: [
|
||||||
|
'rgba(47, 206, 30, 0.92)'
|
||||||
|
],
|
||||||
|
borderColor: [
|
||||||
|
'rgb(47, 171, 34)'
|
||||||
|
],
|
||||||
|
borderWidth: 1
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
scaleShowValues: true,
|
||||||
|
layout: {
|
||||||
|
padding: {
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
top: 0,
|
||||||
|
bottom: -10
|
||||||
|
}
|
||||||
|
},
|
||||||
|
hover: {
|
||||||
|
animationDuration: 0,
|
||||||
|
},
|
||||||
|
responsiveAnimationDuration: 0,
|
||||||
|
animation: {
|
||||||
|
duration: 3500,
|
||||||
|
onComplete: function() {
|
||||||
|
var chartInstance = this.chart,
|
||||||
|
ctx = chartInstance.ctx;
|
||||||
|
|
||||||
|
var controller = this.chart.controller;
|
||||||
|
var xAxis = controller.scales['x-axis-0'];
|
||||||
|
var yAxis = controller.scales['y-axis-0'];
|
||||||
|
|
||||||
|
ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontSize, Chart.defaults.global.defaultFontStyle, Chart.defaults.global.defaultFontFamily);
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.textBaseline = 'bottom';
|
||||||
|
|
||||||
|
var numTicks = xAxis.ticks.length;
|
||||||
|
var yOffsetStart = xAxis.width / numTicks;
|
||||||
|
var halfBarWidth = (xAxis.width / (numTicks * 2));
|
||||||
|
|
||||||
|
xAxis.ticks.forEach(function(value, index) {
|
||||||
|
var xOffset = 20;
|
||||||
|
var yOffset = (yOffsetStart * index) + halfBarWidth;
|
||||||
|
ctx.fillStyle = '#e2e2e2';
|
||||||
|
ctx.fillText(value, yOffset, xOffset);
|
||||||
|
});
|
||||||
|
|
||||||
|
this.data.datasets.forEach(function(dataset, i) {
|
||||||
|
var meta = chartInstance.controller.getDatasetMeta(i);
|
||||||
|
var hxH = 0;
|
||||||
|
var hyH = 0;
|
||||||
|
var hxL = 0;
|
||||||
|
var hyL = 0;
|
||||||
|
var highestNum = 0;
|
||||||
|
var lowestnum = 999999999999;
|
||||||
|
meta.data.forEach(function(bar, index) {
|
||||||
|
var data = dataset.data[index];
|
||||||
|
|
||||||
|
if (data.y {{safe "<"}} lowestnum) {
|
||||||
|
lowestnum = data.y;
|
||||||
|
hxL = bar._model.x;
|
||||||
|
hyL = bar._model.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.y > highestNum) {
|
||||||
|
highestNum = data.y;
|
||||||
|
hxH = bar._model.x;
|
||||||
|
hyH = bar._model.y;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hxH {{safe ">"}}= 820) {
|
||||||
|
hxH = 820;
|
||||||
|
} else if (hxH {{safe "<"}}= 50) {
|
||||||
|
hxH = 50;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hxL {{safe ">"}}= 820) {
|
||||||
|
hxL = 820;
|
||||||
|
} else if (hxL {{safe "<"}}= 70) {
|
||||||
|
hxL = 70;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.fillStyle = '#ffa7a2';
|
||||||
|
ctx.fillText(highestNum+"ms", hxH - 40, hyH + 15);
|
||||||
|
ctx.fillStyle = '#45d642';
|
||||||
|
ctx.fillText(lowestnum+"ms", hxL, hyL + 10);
|
||||||
|
|
||||||
|
console.log("done service_id_{{.Id}}")
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
display: false
|
||||||
|
},
|
||||||
|
tooltips: {
|
||||||
|
"enabled": false
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
yAxes: [{
|
||||||
|
display: false,
|
||||||
|
ticks: {
|
||||||
|
fontSize: 20,
|
||||||
|
display: false,
|
||||||
|
beginAtZero: false
|
||||||
|
},
|
||||||
|
gridLines: {
|
||||||
|
display:false
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
xAxes: [{
|
||||||
|
type: 'time',
|
||||||
|
distribution: 'series',
|
||||||
|
autoSkip: false,
|
||||||
|
gridLines: {
|
||||||
|
display:false
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
stepSize: 1,
|
||||||
|
min: 0,
|
||||||
|
fontColor: "white",
|
||||||
|
fontSize: 20,
|
||||||
|
display: false,
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
elements: {
|
||||||
|
point: {
|
||||||
|
radius: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
{{ end }}
|
||||||
|
{{ end }}
|
|
@ -104,159 +104,8 @@
|
||||||
<script src="/js/bootstrap.min.js"></script>
|
<script src="/js/bootstrap.min.js"></script>
|
||||||
<script src="/js/Chart.bundle.min.js"></script>
|
<script src="/js/Chart.bundle.min.js"></script>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
<script>
|
|
||||||
{{ range .Services }}
|
|
||||||
{{ if .AvgTime }}
|
|
||||||
var ctx = document.getElementById("service_{{.Id}}").getContext('2d');
|
|
||||||
|
|
||||||
var chartdata = new Chart(ctx, {
|
|
||||||
type: 'line',
|
|
||||||
data: {
|
|
||||||
datasets: [{
|
|
||||||
label: 'Response Time (Milliseconds)',
|
|
||||||
data: {{js .GraphData}},
|
|
||||||
backgroundColor: [
|
|
||||||
'rgba(47, 206, 30, 0.92)'
|
|
||||||
],
|
|
||||||
borderColor: [
|
|
||||||
'rgb(47, 171, 34)'
|
|
||||||
],
|
|
||||||
borderWidth: 1
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
scaleShowValues: true,
|
|
||||||
layout: {
|
|
||||||
padding: {
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
top: 0,
|
|
||||||
bottom: -10
|
|
||||||
}
|
|
||||||
},
|
|
||||||
hover: {
|
|
||||||
animationDuration: 0,
|
|
||||||
},
|
|
||||||
responsiveAnimationDuration: 0,
|
|
||||||
animation: {
|
|
||||||
duration: 3500,
|
|
||||||
onComplete: function() {
|
|
||||||
var chartInstance = this.chart,
|
|
||||||
ctx = chartInstance.ctx;
|
|
||||||
|
|
||||||
var controller = this.chart.controller;
|
|
||||||
var xAxis = controller.scales['x-axis-0'];
|
|
||||||
var yAxis = controller.scales['y-axis-0'];
|
|
||||||
|
|
||||||
ctx.font = Chart.helpers.fontString(Chart.defaults.global.defaultFontSize, Chart.defaults.global.defaultFontStyle, Chart.defaults.global.defaultFontFamily);
|
|
||||||
ctx.textAlign = 'center';
|
|
||||||
ctx.textBaseline = 'bottom';
|
|
||||||
|
|
||||||
var numTicks = xAxis.ticks.length;
|
|
||||||
var yOffsetStart = xAxis.width / numTicks;
|
|
||||||
var halfBarWidth = (xAxis.width / (numTicks * 2));
|
|
||||||
|
|
||||||
xAxis.ticks.forEach(function(value, index) {
|
|
||||||
var xOffset = 20;
|
|
||||||
var yOffset = (yOffsetStart * index) + halfBarWidth;
|
|
||||||
ctx.fillStyle = '#e2e2e2';
|
|
||||||
ctx.fillText(value, yOffset, xOffset);
|
|
||||||
});
|
|
||||||
|
|
||||||
this.data.datasets.forEach(function(dataset, i) {
|
|
||||||
var meta = chartInstance.controller.getDatasetMeta(i);
|
|
||||||
var hxH = 0;
|
|
||||||
var hyH = 0;
|
|
||||||
var hxL = 0;
|
|
||||||
var hyL = 0;
|
|
||||||
var highestNum = 0;
|
|
||||||
var lowestnum = 999999999999;
|
|
||||||
meta.data.forEach(function(bar, index) {
|
|
||||||
var data = dataset.data[index];
|
|
||||||
|
|
||||||
if (data.y < lowestnum) {
|
|
||||||
lowestnum = data.y;
|
|
||||||
hxL = bar._model.x;
|
|
||||||
hyL = bar._model.y;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.y > highestNum) {
|
|
||||||
highestNum = data.y;
|
|
||||||
hxH = bar._model.x;
|
|
||||||
hyH = bar._model.y;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (hxH >= 820) {
|
|
||||||
hxH = 820;
|
|
||||||
} else if (hxH <= 50) {
|
|
||||||
hxH = 50;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (hxL >= 820) {
|
|
||||||
hxL = 820;
|
|
||||||
} else if (hxL <= 70) {
|
|
||||||
hxL = 70;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.fillStyle = '#ffa7a2';
|
|
||||||
ctx.fillText(highestNum+"ms", hxH - 40, hyH + 15);
|
|
||||||
ctx.fillStyle = '#45d642';
|
|
||||||
ctx.fillText(lowestnum+"ms", hxL, hyL + 10);
|
|
||||||
|
|
||||||
console.log("done service_id_{{.Id}}")
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
legend: {
|
|
||||||
display: false
|
|
||||||
},
|
|
||||||
tooltips: {
|
|
||||||
"enabled": false
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
yAxes: [{
|
|
||||||
display: false,
|
|
||||||
ticks: {
|
|
||||||
fontSize: 20,
|
|
||||||
display: false,
|
|
||||||
beginAtZero: false
|
|
||||||
},
|
|
||||||
gridLines: {
|
|
||||||
display:false
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
xAxes: [{
|
|
||||||
type: 'time',
|
|
||||||
distribution: 'series',
|
|
||||||
autoSkip: false,
|
|
||||||
gridLines: {
|
|
||||||
display:false
|
|
||||||
},
|
|
||||||
ticks: {
|
|
||||||
stepSize: 1,
|
|
||||||
min: 0,
|
|
||||||
fontColor: "white",
|
|
||||||
fontSize: 20,
|
|
||||||
display: false,
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
elements: {
|
|
||||||
point: {
|
|
||||||
radius: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
{{ end }}
|
|
||||||
{{ end }}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script src="/js/main.js"></script>
|
<script src="/js/main.js"></script>
|
||||||
|
<script src="/charts.js"></script>
|
||||||
|
|
||||||
{{ if .Core.Style }}
|
{{ if .Core.Style }}
|
||||||
<style>
|
<style>
|
||||||
|
|
Loading…
Reference in New Issue