mirror of https://github.com/statping/statping
service api data - charts js updates (ajax rendering
parent
aba8456b90
commit
b24f939541
2
Makefile
2
Makefile
|
@ -1,4 +1,4 @@
|
|||
VERSION=0.70
|
||||
VERSION=0.71
|
||||
BINARY_NAME=statup
|
||||
GOPATH:=$(GOPATH)
|
||||
GOCMD=go
|
||||
|
|
|
@ -67,9 +67,9 @@ func checkinDB() *gorm.DB {
|
|||
}
|
||||
|
||||
// HitsBetween returns the gorm database query for a collection of service hits between a time range
|
||||
func (s *Service) HitsBetween(t1, t2 time.Time) *gorm.DB {
|
||||
selector := Dbtimestamp(3600)
|
||||
return DbSession.Model(&types.Hit{}).Select(selector).Where("service = ? AND created_at BETWEEN ? AND ?", s.Id, t1.UTC().Format(types.TIME), t2.UTC().Format(types.TIME)).Group("timeframe")
|
||||
func (s *Service) HitsBetween(t1, t2 time.Time, group string) *gorm.DB {
|
||||
selector := Dbtimestamp(group)
|
||||
return DbSession.Debug().Model(&types.Hit{}).Select(selector).Where("service = ? AND created_at BETWEEN ? AND ?", s.Id, t1.Format(types.TIME_DAY), t2.Format(types.TIME_DAY)).Order("timeframe asc", false).Group("timeframe")
|
||||
}
|
||||
|
||||
func CloseDB() {
|
||||
|
|
|
@ -128,7 +128,7 @@ type DateScan struct {
|
|||
|
||||
// DateScanObj struct is for creating the charts.js graph JSON array
|
||||
type DateScanObj struct {
|
||||
Array []DateScan
|
||||
Array []DateScan `json:"data"`
|
||||
}
|
||||
|
||||
// lastFailure returns the last failure a service had
|
||||
|
@ -166,12 +166,14 @@ func (s *Service) DowntimeText() string {
|
|||
return fmt.Sprintf("%v has been offline for %v", s.Name, utils.DurationReadable(s.Downtime()))
|
||||
}
|
||||
|
||||
func Dbtimestamp(seconds int64) string {
|
||||
incrementTime := "second"
|
||||
if seconds == 60 {
|
||||
incrementTime = "minute"
|
||||
} else if seconds == 3600 {
|
||||
incrementTime = "hour"
|
||||
func Dbtimestamp(group string) string {
|
||||
seconds := 60
|
||||
if group == "second" {
|
||||
seconds = 60
|
||||
} else if group == "hour" {
|
||||
seconds = 3600
|
||||
} else if group == "day" {
|
||||
seconds = 86400
|
||||
}
|
||||
switch CoreApp.DbConnection {
|
||||
case "mysql":
|
||||
|
@ -179,7 +181,7 @@ func Dbtimestamp(seconds int64) string {
|
|||
case "sqlite":
|
||||
return fmt.Sprintf("datetime((strftime('%%s', created_at) / %v) * %v, 'unixepoch') AS timeframe, AVG(latency) as value", seconds, seconds)
|
||||
case "postgres":
|
||||
return fmt.Sprintf("date_trunc('%v', created_at) AS timeframe, AVG(latency) AS value", incrementTime)
|
||||
return fmt.Sprintf("date_trunc('%v', created_at) AS timeframe, AVG(latency) AS value", group)
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
@ -199,16 +201,20 @@ func (s *Service) Downtime() time.Duration {
|
|||
return since
|
||||
}
|
||||
|
||||
func GraphDataRaw(service types.ServiceInterface, start, end time.Time) *DateScanObj {
|
||||
func GraphDataRaw(service types.ServiceInterface, start, end time.Time, group string) *DateScanObj {
|
||||
var d []DateScan
|
||||
model := service.(*Service).HitsBetween(start, end)
|
||||
model := service.(*Service).HitsBetween(start, end, group)
|
||||
rows, _ := model.Rows()
|
||||
for rows.Next() {
|
||||
var gd DateScan
|
||||
var createdAt string
|
||||
var value float64
|
||||
var createdTime time.Time
|
||||
rows.Scan(&createdAt, &value)
|
||||
createdTime, _ := time.Parse(types.TIME, createdAt)
|
||||
createdTime, _ = time.Parse(types.TIME, createdAt)
|
||||
if CoreApp.DbConnection == "postgres" {
|
||||
createdTime, _ = time.Parse(types.TIME_NANO, createdAt)
|
||||
}
|
||||
gd.CreatedAt = utils.Timezoner(createdTime, CoreApp.Timezone).Format(types.TIME)
|
||||
gd.Value = int64(value * 1000)
|
||||
d = append(d, gd)
|
||||
|
@ -227,9 +233,9 @@ func (d *DateScanObj) ToString() string {
|
|||
|
||||
// GraphData returns the JSON object used by Charts.js to render the chart
|
||||
func (s *Service) GraphData() string {
|
||||
start := time.Now().Add(-24 * time.Hour)
|
||||
start := time.Now().Add((-24 * 7) * time.Hour)
|
||||
end := time.Now()
|
||||
obj := GraphDataRaw(s, start, end)
|
||||
obj := GraphDataRaw(s, start, end, "hour")
|
||||
data, err := json.Marshal(obj)
|
||||
if err != nil {
|
||||
utils.Log(2, err)
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
"github.com/hunterlong/statup/utils"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
)
|
||||
|
||||
type ApiResponse struct {
|
||||
|
@ -77,6 +78,23 @@ func apiCheckinHandler(w http.ResponseWriter, r *http.Request) {
|
|||
json.NewEncoder(w).Encode(checkin)
|
||||
}
|
||||
|
||||
func apiServiceDataHandler(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
service := core.SelectService(utils.StringInt(vars["id"]))
|
||||
if service == nil {
|
||||
http.Error(w, http.StatusText(http.StatusNotFound), http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
fields := parseGet(r)
|
||||
grouping := fields.Get("group")
|
||||
startField := utils.StringInt(fields.Get("start"))
|
||||
endField := utils.StringInt(fields.Get("end"))
|
||||
obj := core.GraphDataRaw(service, time.Unix(startField, 0), time.Unix(endField, 0), grouping)
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(obj)
|
||||
}
|
||||
|
||||
func apiServiceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
if !isAPIAuthorized(r) {
|
||||
http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
|
||||
|
|
|
@ -237,6 +237,20 @@ func TestApiDeleteUserHandler(t *testing.T) {
|
|||
assert.Equal(t, "success", obj.Status)
|
||||
}
|
||||
|
||||
func TestApiServiceDataHandler(t *testing.T) {
|
||||
grouping := []string{"minute", "hour", "day"}
|
||||
for _, g := range grouping {
|
||||
params := "?start=0&end=999999999999&group=" + g
|
||||
rr, err := httpRequestAPI(t, "GET", "/api/services/1/data"+params, nil)
|
||||
assert.Nil(t, err)
|
||||
body := rr.Body.String()
|
||||
var obj core.DateScanObj
|
||||
formatJSON(body, &obj)
|
||||
assert.Equal(t, 200, rr.Code)
|
||||
assert.NotZero(t, len(obj.Array))
|
||||
}
|
||||
}
|
||||
|
||||
func httpRequestAPI(t *testing.T, method, url string, body io.Reader) (*httptest.ResponseRecorder, error) {
|
||||
req, err := http.NewRequest(method, url, body)
|
||||
if err != nil {
|
||||
|
|
|
@ -84,26 +84,13 @@ func IsAuthenticated(r *http.Request) bool {
|
|||
return session.Values["authenticated"].(bool)
|
||||
}
|
||||
|
||||
// executeResponse will render a HTTP response for the front end user
|
||||
func executeResponse(w http.ResponseWriter, r *http.Request, file string, data interface{}, redirect interface{}) {
|
||||
utils.Http(r)
|
||||
if url, ok := redirect.(string); ok {
|
||||
http.Redirect(w, r, url, http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
nav, _ := source.TmplBox.String("nav.html")
|
||||
footer, _ := source.TmplBox.String("footer.html")
|
||||
render, err := source.TmplBox.String(file)
|
||||
if err != nil {
|
||||
utils.Log(4, err)
|
||||
}
|
||||
t := template.New("message")
|
||||
t.Funcs(template.FuncMap{
|
||||
"js": func(html string) template.JS {
|
||||
return template.JS(html)
|
||||
var handlerFuncs = func(w http.ResponseWriter, r *http.Request) template.FuncMap {
|
||||
return template.FuncMap{
|
||||
"js": func(html interface{}) template.JS {
|
||||
return template.JS(utils.ToString(html))
|
||||
},
|
||||
"safe": func(html string) template.HTML {
|
||||
return template.HTML(html)
|
||||
"safe": func(html interface{}) template.HTML {
|
||||
return template.HTML(utils.ToString(html))
|
||||
},
|
||||
"Auth": func() bool {
|
||||
return IsAuthenticated(r)
|
||||
|
@ -156,7 +143,25 @@ func executeResponse(w http.ResponseWriter, r *http.Request, file string, data i
|
|||
"FromUnix": func(t int64) string {
|
||||
return utils.Timezoner(time.Unix(t, 0), core.CoreApp.Timezone).Format("Monday, January 02")
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// executeResponse will render a HTTP response for the front end user
|
||||
func executeResponse(w http.ResponseWriter, r *http.Request, file string, data interface{}, redirect interface{}) {
|
||||
utils.Http(r)
|
||||
if url, ok := redirect.(string); ok {
|
||||
http.Redirect(w, r, url, http.StatusSeeOther)
|
||||
return
|
||||
}
|
||||
nav, _ := source.TmplBox.String("nav.html")
|
||||
footer, _ := source.TmplBox.String("footer.html")
|
||||
chartIndex, _ := source.JsBox.String("chart_index.js")
|
||||
render, err := source.TmplBox.String(file)
|
||||
if err != nil {
|
||||
utils.Log(4, err)
|
||||
}
|
||||
t := template.New("message")
|
||||
t.Funcs(handlerFuncs(w, r))
|
||||
t, err = t.Parse(nav)
|
||||
if err != nil {
|
||||
utils.Log(4, err)
|
||||
|
@ -169,6 +174,15 @@ func executeResponse(w http.ResponseWriter, r *http.Request, file string, data i
|
|||
if err != nil {
|
||||
utils.Log(4, err)
|
||||
}
|
||||
_, err = t.Parse(chartIndex)
|
||||
if err != nil {
|
||||
utils.Log(4, err)
|
||||
}
|
||||
fmt.Println(t.Templates())
|
||||
fmt.Println(t.DefinedTemplates())
|
||||
|
||||
t.Lookup("chartIndex").Funcs(handlerFuncs(w, r))
|
||||
|
||||
err = t.Execute(w, data)
|
||||
if err != nil {
|
||||
utils.Log(4, err)
|
||||
|
|
|
@ -86,6 +86,7 @@ func Router() *mux.Router {
|
|||
r.Handle("/api/services", http.HandlerFunc(apiAllServicesHandler)).Methods("GET")
|
||||
r.Handle("/api/services", http.HandlerFunc(apiCreateServiceHandler)).Methods("POST")
|
||||
r.Handle("/api/services/{id}", http.HandlerFunc(apiServiceHandler)).Methods("GET")
|
||||
r.Handle("/api/services/{id}/data", http.HandlerFunc(apiServiceDataHandler)).Methods("GET")
|
||||
r.Handle("/api/services/{id}", http.HandlerFunc(apiServiceUpdateHandler)).Methods("POST")
|
||||
r.Handle("/api/services/{id}", http.HandlerFunc(apiServiceDeleteHandler)).Methods("DELETE")
|
||||
|
||||
|
|
|
@ -54,7 +54,7 @@ func renderServiceChartHandler(w http.ResponseWriter, r *http.Request) {
|
|||
}
|
||||
|
||||
service := core.SelectService(utils.StringInt(vars["id"]))
|
||||
data := core.GraphDataRaw(service, start, end).ToString()
|
||||
data := core.GraphDataRaw(service, start, end, "hour").ToString()
|
||||
|
||||
out := struct {
|
||||
Services []*core.Service
|
||||
|
@ -74,7 +74,7 @@ func renderServiceChartsHandler(w http.ResponseWriter, r *http.Request) {
|
|||
start := now.BeginningOfDay().UTC()
|
||||
|
||||
for _, s := range services {
|
||||
d := core.GraphDataRaw(s, start, end).ToString()
|
||||
d := core.GraphDataRaw(s, start, end, "hour").ToString()
|
||||
data = append(data, d)
|
||||
}
|
||||
|
||||
|
@ -194,7 +194,7 @@ func servicesViewHandler(w http.ResponseWriter, r *http.Request) {
|
|||
end = time.Unix(endField, 0)
|
||||
}
|
||||
|
||||
data := core.GraphDataRaw(serv, start, end)
|
||||
data := core.GraphDataRaw(serv, start, end, "hour")
|
||||
|
||||
out := struct {
|
||||
Service *core.Service
|
||||
|
|
|
@ -0,0 +1,137 @@
|
|||
{{define "chartIndex"}}
|
||||
var ctx_{{js .Id}} = document.getElementById("service_{{js .Id}}").getContext('2d');
|
||||
var chartdata_{{js .Id}} = new Chart(ctx_{{js .Id}}, {
|
||||
type: 'line',
|
||||
data: {
|
||||
datasets: [{
|
||||
label: 'Response Time (Milliseconds)',
|
||||
data: [],
|
||||
backgroundColor: ['rgba(47, 206, 30, 0.92)'],
|
||||
borderColor: ['rgb(47, 171, 34)'],
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
maintainAspectRatio: !1,
|
||||
scaleShowValues: !0,
|
||||
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 (lowestnum > data.y) {
|
||||
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 (50 >= hxH) {
|
||||
hxH = 50
|
||||
}
|
||||
if (hxL >= 820) {
|
||||
hxL = 820
|
||||
} else if (70 >= hxL) {
|
||||
hxL = 70
|
||||
}
|
||||
ctx.fillStyle = '#ffa7a2';
|
||||
ctx.fillText(highestNum + "ms", hxH - 40, hyH + 15);
|
||||
ctx.fillStyle = '#45d642';
|
||||
ctx.fillText(lowestnum + "ms", hxL, hyL + 10);
|
||||
})
|
||||
}
|
||||
},
|
||||
legend: {
|
||||
display: !1
|
||||
},
|
||||
tooltips: {
|
||||
"enabled": !1
|
||||
},
|
||||
scales: {
|
||||
yAxes: [{
|
||||
display: !1,
|
||||
ticks: {
|
||||
fontSize: 20,
|
||||
display: !1,
|
||||
beginAtZero: !1
|
||||
},
|
||||
gridLines: {
|
||||
display: !1
|
||||
}
|
||||
}],
|
||||
xAxes: [{
|
||||
type: 'time',
|
||||
distribution: 'series',
|
||||
autoSkip: !1,
|
||||
time: {
|
||||
displayFormats: {
|
||||
'hour': 'MMM DD hA'
|
||||
},
|
||||
source: 'auto'
|
||||
},
|
||||
gridLines: {
|
||||
display: !1
|
||||
},
|
||||
ticks: {
|
||||
source: 'auto',
|
||||
stepSize: 1,
|
||||
min: 0,
|
||||
fontColor: "white",
|
||||
fontSize: 20,
|
||||
display: !1
|
||||
}
|
||||
}]
|
||||
},
|
||||
elements: {
|
||||
point: {
|
||||
radius: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
AjaxChart(chartdata_{{js .Id}},{{js .Id}},0,99999999999,"hour");
|
||||
{{end}}
|
|
@ -18,6 +18,6 @@
|
|||
{{$d := .Data}}{{ range $i, $s := .Services }}{{ if $s.AvgTime }}var ctx_{{$s.Id}}=document.getElementById("service_{{$s.Id}}").getContext('2d');var chartdata=new Chart(ctx_{{$s.Id}},{type:'line',data:{datasets:[{label:'Response Time (Milliseconds)',data:{{safe (index $d $i)}},backgroundColor:['rgba(47, 206, 30, 0.92)'],borderColor:['rgb(47, 171, 34)'],borderWidth:1}]},options:{maintainAspectRatio:!1,scaleShowValues:!0,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(lowestnum>data.y){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(50>=hxH){hxH=50}
|
||||
if(hxL>=820){hxL=820}else if(70>=hxL){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:!1},tooltips:{"enabled":!1},scales:{yAxes:[{display:!1,ticks:{fontSize:20,display:!1,beginAtZero:!1},gridLines:{display:!1}}],xAxes:[{type:'time',distribution:'series',autoSkip:!1,gridLines:{display:!1},ticks:{stepSize:1,min:0,fontColor:"white",fontSize:20,display:!1,}}]},elements:{point:{radius:0}}}})
|
||||
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:!1},tooltips:{"enabled":!1},scales:{yAxes:[{display:!1,ticks:{fontSize:20,display:!1,beginAtZero:!1},gridLines:{display:!1}}],xAxes:[{type:'time',distribution:'series',autoSkip:!1,time:{displayFormats:{'hour': 'MMM DD hA'},source: 'auto'},gridLines:{display:!1},ticks:{source:'auto',stepSize:1,min:0,fontColor:"white",fontSize:20,display:!1}}]},elements:{point:{radius:0}}}})
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
|
|
@ -81,6 +81,20 @@ $('select#service_type').on('change', function() {
|
|||
|
||||
});
|
||||
|
||||
function AjaxChart(chart, service, start=0, end=9999999999, group="hour") {
|
||||
$.ajax({
|
||||
url: "/api/services/"+service+"/data?start="+start+"&end="+end+"&group="+group,
|
||||
type: 'GET',
|
||||
success: function(data) {
|
||||
chart.data.labels.pop();
|
||||
data.data.forEach(function(d) {
|
||||
chart.data.datasets[0].data.push(d);
|
||||
});
|
||||
chart.update();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$('select#service_check_type').on('change', function() {
|
||||
var selected = $('#service_check_type option:selected').val();
|
||||
if (selected === 'POST') {
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, maximum-scale=1.0, user-scalable=0">
|
||||
{{if USE_CDN}}
|
||||
<link rel="shortcut icon" type="image/x-icon" href="https://assets.statup.io/favicon.ico">
|
||||
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" integrity="sha384-WskhaSGFgHYWDcbwN70/dfYBj47jz9qbsMId/iRN3ewGhXQFZCSftd1LZCfmhktB" crossorigin="anonymous">
|
||||
<link rel="stylesheet" href="https://assets.statup.io/base.css">
|
||||
{{ else }}
|
||||
<link rel="shortcut icon" type="image/x-icon" href="/favicon.ico">
|
||||
<link rel="stylesheet" href="/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" href="/css/base.css">
|
||||
{{end}}
|
||||
|
||||
<title>{{.Name}} Status</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -112,18 +112,17 @@
|
|||
<script src="/js/main.js"></script>
|
||||
{{end}}
|
||||
|
||||
{{if CHART_DATA}}
|
||||
<script>{{ js CHART_DATA }}</script>
|
||||
{{ else }}
|
||||
<script src="/charts.js"></script>
|
||||
{{ if .Style }}
|
||||
<style>
|
||||
{{ safe .Style }}
|
||||
</style>
|
||||
{{ end }}
|
||||
|
||||
{{ if .Style }}
|
||||
<style>
|
||||
{{ safe .Style }}
|
||||
</style>
|
||||
{{ end }}
|
||||
|
||||
<script>
|
||||
{{ range Services }}
|
||||
{{template "chartIndex" .}}
|
||||
{{end}}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -255,7 +255,7 @@
|
|||
data: {
|
||||
datasets: [{
|
||||
label: 'Response Time (Milliseconds)',
|
||||
data: {{js .Data}},
|
||||
data: [],
|
||||
backgroundColor: [
|
||||
'rgba(47, 206, 30, 0.92)'
|
||||
],
|
||||
|
@ -280,10 +280,27 @@
|
|||
}],
|
||||
xAxes: [{
|
||||
type: 'time',
|
||||
distribution: 'series',
|
||||
time: {
|
||||
displayFormats: {
|
||||
'millisecond': 'MMM DD',
|
||||
'second': 'MMM DD',
|
||||
'minute': 'MMM DD',
|
||||
'hour': 'MMM DD hA',
|
||||
'day': 'MMM DD',
|
||||
'week': 'MMM DD',
|
||||
'month': 'MMM DD',
|
||||
'quarter': 'MMM DD',
|
||||
'year': 'MMM DD',
|
||||
}
|
||||
},
|
||||
gridLines: {
|
||||
display: true
|
||||
},
|
||||
ticks: {
|
||||
source: 'auto'
|
||||
}
|
||||
}]
|
||||
}],
|
||||
},
|
||||
elements: {
|
||||
point: {
|
||||
|
@ -332,6 +349,8 @@
|
|||
endPick.show()
|
||||
})
|
||||
|
||||
AjaxChart(chartdata,{{$s.Id}},{{.Start}},{{.End}},"hour");
|
||||
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
|
|
@ -23,6 +23,7 @@ const (
|
|||
TIME_NANOZ = "2006-01-02 15:04:05.999999-0700 MST"
|
||||
TIME_NANO = "2006-01-02T15:04:05Z"
|
||||
TIME = "2006-01-02 15:04:05"
|
||||
TIME_DAY = "2006-01-02"
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
Loading…
Reference in New Issue