mirror of https://github.com/statping/statping
apexcharts - heatmap
parent
652b565d4e
commit
30b6a2df6b
|
@ -244,6 +244,7 @@ func recordFailure(s *Service, issue string) {
|
||||||
Issue: issue,
|
Issue: issue,
|
||||||
PingTime: s.PingTime,
|
PingTime: s.PingTime,
|
||||||
CreatedAt: time.Now(),
|
CreatedAt: time.Now(),
|
||||||
|
ErrorCode: s.LastStatusCode,
|
||||||
}}
|
}}
|
||||||
utils.Log(2, fmt.Sprintf("Service %v Failing: %v | Lookup in: %0.2f ms", s.Name, issue, fail.PingTime*1000))
|
utils.Log(2, fmt.Sprintf("Service %v Failing: %v | Lookup in: %0.2f ms", s.Name, issue, fail.PingTime*1000))
|
||||||
s.CreateFailure(fail)
|
s.CreateFailure(fail)
|
||||||
|
|
|
@ -124,6 +124,16 @@ func CountFailures() uint64 {
|
||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TotalFailuresOnDate returns the total amount of failures for a service on a specific time/date
|
||||||
|
func (s *Service) TotalFailuresOnDate(ago time.Time) (uint64, error) {
|
||||||
|
var count uint64
|
||||||
|
date := ago.UTC().Format("2006-01-02 00:00:00")
|
||||||
|
dateend := ago.UTC().Format("2006-01-02 23:59:59")
|
||||||
|
rows := failuresDB().Where("service = ? AND created_at BETWEEN ? AND ?", s.Id, date, dateend).Not("method = 'checkin'")
|
||||||
|
err := rows.Count(&count)
|
||||||
|
return count, err.Error
|
||||||
|
}
|
||||||
|
|
||||||
// TotalFailures24 returns the amount of failures for a service within the last 24 hours
|
// TotalFailures24 returns the amount of failures for a service within the last 24 hours
|
||||||
func (s *Service) TotalFailures24() (uint64, error) {
|
func (s *Service) TotalFailures24() (uint64, error) {
|
||||||
ago := time.Now().Add(-24 * time.Hour)
|
ago := time.Now().Add(-24 * time.Hour)
|
||||||
|
|
|
@ -28,7 +28,7 @@ func InsertSampleData() error {
|
||||||
utils.Log(1, "Inserting Sample Data...")
|
utils.Log(1, "Inserting Sample Data...")
|
||||||
|
|
||||||
insertSampleGroups()
|
insertSampleGroups()
|
||||||
|
createdOn := time.Now().Add((-24 * 90) * time.Hour).UTC()
|
||||||
s1 := ReturnService(&types.Service{
|
s1 := ReturnService(&types.Service{
|
||||||
Name: "Google",
|
Name: "Google",
|
||||||
Domain: "https://google.com",
|
Domain: "https://google.com",
|
||||||
|
@ -39,6 +39,7 @@ func InsertSampleData() error {
|
||||||
Timeout: 10,
|
Timeout: 10,
|
||||||
Order: 1,
|
Order: 1,
|
||||||
GroupId: 1,
|
GroupId: 1,
|
||||||
|
CreatedAt: createdOn,
|
||||||
})
|
})
|
||||||
s2 := ReturnService(&types.Service{
|
s2 := ReturnService(&types.Service{
|
||||||
Name: "Statping Github",
|
Name: "Statping Github",
|
||||||
|
@ -49,6 +50,7 @@ func InsertSampleData() error {
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
Timeout: 20,
|
Timeout: 20,
|
||||||
Order: 2,
|
Order: 2,
|
||||||
|
CreatedAt: createdOn,
|
||||||
})
|
})
|
||||||
s3 := ReturnService(&types.Service{
|
s3 := ReturnService(&types.Service{
|
||||||
Name: "JSON Users Test",
|
Name: "JSON Users Test",
|
||||||
|
@ -61,6 +63,7 @@ func InsertSampleData() error {
|
||||||
Order: 3,
|
Order: 3,
|
||||||
Public: types.NewNullBool(true),
|
Public: types.NewNullBool(true),
|
||||||
GroupId: 2,
|
GroupId: 2,
|
||||||
|
CreatedAt: createdOn,
|
||||||
})
|
})
|
||||||
s4 := ReturnService(&types.Service{
|
s4 := ReturnService(&types.Service{
|
||||||
Name: "JSON API Tester",
|
Name: "JSON API Tester",
|
||||||
|
@ -75,17 +78,19 @@ func InsertSampleData() error {
|
||||||
Order: 4,
|
Order: 4,
|
||||||
Public: types.NewNullBool(true),
|
Public: types.NewNullBool(true),
|
||||||
GroupId: 2,
|
GroupId: 2,
|
||||||
|
CreatedAt: createdOn,
|
||||||
})
|
})
|
||||||
s5 := ReturnService(&types.Service{
|
s5 := ReturnService(&types.Service{
|
||||||
Name: "Google DNS",
|
Name: "Google DNS",
|
||||||
Domain: "8.8.8.8",
|
Domain: "8.8.8.8",
|
||||||
Interval: 20,
|
Interval: 20,
|
||||||
Type: "tcp",
|
Type: "tcp",
|
||||||
Port: 53,
|
Port: 53,
|
||||||
Timeout: 120,
|
Timeout: 120,
|
||||||
Order: 5,
|
Order: 5,
|
||||||
Public: types.NewNullBool(true),
|
Public: types.NewNullBool(true),
|
||||||
GroupId: 1,
|
GroupId: 1,
|
||||||
|
CreatedAt: createdOn,
|
||||||
})
|
})
|
||||||
|
|
||||||
s1.Create(false)
|
s1.Create(false)
|
||||||
|
@ -249,6 +254,7 @@ func InsertLargeSampleData() error {
|
||||||
if err := insertMessages(); err != nil {
|
if err := insertMessages(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
createdOn := time.Now().Add((-24 * 90) * time.Hour).UTC()
|
||||||
s6 := ReturnService(&types.Service{
|
s6 := ReturnService(&types.Service{
|
||||||
Name: "JSON Lint",
|
Name: "JSON Lint",
|
||||||
Domain: "https://jsonlint.com",
|
Domain: "https://jsonlint.com",
|
||||||
|
@ -258,6 +264,7 @@ func InsertLargeSampleData() error {
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
Timeout: 10,
|
Timeout: 10,
|
||||||
Order: 6,
|
Order: 6,
|
||||||
|
CreatedAt: createdOn,
|
||||||
})
|
})
|
||||||
|
|
||||||
s7 := ReturnService(&types.Service{
|
s7 := ReturnService(&types.Service{
|
||||||
|
@ -269,6 +276,7 @@ func InsertLargeSampleData() error {
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
Timeout: 15,
|
Timeout: 15,
|
||||||
Order: 7,
|
Order: 7,
|
||||||
|
CreatedAt: createdOn,
|
||||||
})
|
})
|
||||||
|
|
||||||
s8 := ReturnService(&types.Service{
|
s8 := ReturnService(&types.Service{
|
||||||
|
@ -291,6 +299,7 @@ func InsertLargeSampleData() error {
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
Timeout: 10,
|
Timeout: 10,
|
||||||
Order: 9,
|
Order: 9,
|
||||||
|
CreatedAt: createdOn,
|
||||||
})
|
})
|
||||||
|
|
||||||
s10 := ReturnService(&types.Service{
|
s10 := ReturnService(&types.Service{
|
||||||
|
@ -302,6 +311,7 @@ func InsertLargeSampleData() error {
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
Timeout: 10,
|
Timeout: 10,
|
||||||
Order: 10,
|
Order: 10,
|
||||||
|
CreatedAt: createdOn,
|
||||||
})
|
})
|
||||||
|
|
||||||
s11 := ReturnService(&types.Service{
|
s11 := ReturnService(&types.Service{
|
||||||
|
@ -313,6 +323,7 @@ func InsertLargeSampleData() error {
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
Timeout: 20,
|
Timeout: 20,
|
||||||
Order: 11,
|
Order: 11,
|
||||||
|
CreatedAt: createdOn,
|
||||||
})
|
})
|
||||||
|
|
||||||
s12 := ReturnService(&types.Service{
|
s12 := ReturnService(&types.Service{
|
||||||
|
@ -324,6 +335,7 @@ func InsertLargeSampleData() error {
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
Timeout: 20,
|
Timeout: 20,
|
||||||
Order: 12,
|
Order: 12,
|
||||||
|
CreatedAt: createdOn,
|
||||||
})
|
})
|
||||||
|
|
||||||
s13 := ReturnService(&types.Service{
|
s13 := ReturnService(&types.Service{
|
||||||
|
@ -335,6 +347,7 @@ func InsertLargeSampleData() error {
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
Timeout: 10,
|
Timeout: 10,
|
||||||
Order: 13,
|
Order: 13,
|
||||||
|
CreatedAt: createdOn,
|
||||||
})
|
})
|
||||||
|
|
||||||
s14 := ReturnService(&types.Service{
|
s14 := ReturnService(&types.Service{
|
||||||
|
@ -346,6 +359,7 @@ func InsertLargeSampleData() error {
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
Timeout: 12,
|
Timeout: 12,
|
||||||
Order: 14,
|
Order: 14,
|
||||||
|
CreatedAt: createdOn,
|
||||||
})
|
})
|
||||||
|
|
||||||
s15 := ReturnService(&types.Service{
|
s15 := ReturnService(&types.Service{
|
||||||
|
@ -357,6 +371,7 @@ func InsertLargeSampleData() error {
|
||||||
Method: "GET",
|
Method: "GET",
|
||||||
Timeout: 12,
|
Timeout: 12,
|
||||||
Order: 15,
|
Order: 15,
|
||||||
|
CreatedAt: createdOn,
|
||||||
})
|
})
|
||||||
|
|
||||||
s6.Create(false)
|
s6.Create(false)
|
||||||
|
@ -370,7 +385,7 @@ func InsertLargeSampleData() error {
|
||||||
s14.Create(false)
|
s14.Create(false)
|
||||||
s15.Create(false)
|
s15.Create(false)
|
||||||
|
|
||||||
var dayAgo = time.Now().Add(-24 * time.Hour).Add(-10 * time.Minute)
|
var dayAgo = time.Now().Add((-24 * 90) * time.Hour)
|
||||||
|
|
||||||
insertHitRecords(dayAgo, 1450)
|
insertHitRecords(dayAgo, 1450)
|
||||||
|
|
||||||
|
|
|
@ -103,6 +103,7 @@ func Router() *mux.Router {
|
||||||
r.Handle("/api/reorder", http.HandlerFunc(reorderServiceHandler)).Methods("POST")
|
r.Handle("/api/reorder", http.HandlerFunc(reorderServiceHandler)).Methods("POST")
|
||||||
r.Handle("/api/services/{id}/data", cached("30s", "application/json", http.HandlerFunc(apiServiceDataHandler))).Methods("GET")
|
r.Handle("/api/services/{id}/data", cached("30s", "application/json", http.HandlerFunc(apiServiceDataHandler))).Methods("GET")
|
||||||
r.Handle("/api/services/{id}/ping", http.HandlerFunc(apiServicePingDataHandler)).Methods("GET")
|
r.Handle("/api/services/{id}/ping", http.HandlerFunc(apiServicePingDataHandler)).Methods("GET")
|
||||||
|
r.Handle("/api/services/{id}/heatmap", http.HandlerFunc(apiServiceHeatmapHandler)).Methods("GET")
|
||||||
r.Handle("/api/services/{id}", http.HandlerFunc(apiServiceUpdateHandler)).Methods("POST")
|
r.Handle("/api/services/{id}", http.HandlerFunc(apiServiceUpdateHandler)).Methods("POST")
|
||||||
r.Handle("/api/services/{id}", http.HandlerFunc(apiServiceDeleteHandler)).Methods("DELETE")
|
r.Handle("/api/services/{id}", http.HandlerFunc(apiServiceDeleteHandler)).Methods("DELETE")
|
||||||
r.Handle("/api/services/{id}/failures", http.HandlerFunc(apiServiceFailuresHandler)).Methods("GET")
|
r.Handle("/api/services/{id}/failures", http.HandlerFunc(apiServiceFailuresHandler)).Methods("GET")
|
||||||
|
|
|
@ -221,6 +221,53 @@ func apiServicePingDataHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
json.NewEncoder(w).Encode(obj)
|
json.NewEncoder(w).Encode(obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type dataXy struct {
|
||||||
|
X int `json:"x"`
|
||||||
|
Y int `json:"y"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type dataXyMonth struct {
|
||||||
|
Date time.Time `json:"date"`
|
||||||
|
Data []*dataXy `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func apiServiceHeatmapHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
vars := mux.Vars(r)
|
||||||
|
service := core.SelectService(utils.ToInt(vars["id"]))
|
||||||
|
if service == nil {
|
||||||
|
sendErrorJson(errors.New("service data not found"), w, r)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var monthOutput []*dataXyMonth
|
||||||
|
|
||||||
|
start := service.CreatedAt
|
||||||
|
|
||||||
|
if start.Year() <= 2 {
|
||||||
|
start = service.CreatedAt.Add(time.Duration((-3 * 24) * time.Hour))
|
||||||
|
}
|
||||||
|
|
||||||
|
for y := start; y.Year() == start.Year(); y = y.AddDate(1, 0, 0) {
|
||||||
|
|
||||||
|
for m := y; m.Month() == y.Month(); m = m.AddDate(0, 1, 0) {
|
||||||
|
|
||||||
|
var output []*dataXy
|
||||||
|
|
||||||
|
for day := 1; day <= 31; day++ {
|
||||||
|
date := time.Date(y.Year(), y.Month(), day, 0, 0, 0, 0, time.UTC)
|
||||||
|
failures, _ := service.TotalFailuresOnDate(date)
|
||||||
|
output = append(output, &dataXy{day, int(failures)})
|
||||||
|
}
|
||||||
|
|
||||||
|
monthOutput = append(monthOutput, &dataXyMonth{m, output})
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json")
|
||||||
|
json.NewEncoder(w).Encode(monthOutput)
|
||||||
|
}
|
||||||
|
|
||||||
func apiServiceDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
func apiServiceDeleteHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if !IsFullAuthenticated(r) {
|
if !IsFullAuthenticated(r) {
|
||||||
sendUnauthorizedJson(w, r)
|
sendUnauthorizedJson(w, r)
|
||||||
|
|
|
@ -139,6 +139,7 @@ HTML, BODY {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 170px;
|
height: 170px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.service-chart-container {
|
.service-chart-container {
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,86 +1,139 @@
|
||||||
{{define "charts"}}
|
{{define "charts"}}
|
||||||
{{$start := .Start}}
|
{{$start := .Start}}
|
||||||
{{$end := .End}}
|
{{$end := .End}}
|
||||||
|
|
||||||
|
const axisOptions = {
|
||||||
|
labels: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
crosshairs: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
lines: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
enabled: false
|
||||||
|
},
|
||||||
|
axisTicks: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
marker: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let options = {
|
||||||
|
chart: {
|
||||||
|
height: 210,
|
||||||
|
width: "100%",
|
||||||
|
type: "area",
|
||||||
|
animations: {
|
||||||
|
enabled: false,
|
||||||
|
initialAnimation: {
|
||||||
|
enabled: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
selection: {
|
||||||
|
enabled: false
|
||||||
|
},
|
||||||
|
zoom: {
|
||||||
|
enabled: false
|
||||||
|
},
|
||||||
|
toolbar: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
show: false,
|
||||||
|
padding: {
|
||||||
|
top: 0,
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
enabled: false,
|
||||||
|
marker: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
x: {
|
||||||
|
show: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
dataLabels: {
|
||||||
|
enabled: false
|
||||||
|
},
|
||||||
|
floating: true,
|
||||||
|
axisTicks: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
axisBorder: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
fill: {
|
||||||
|
colors: ["#48d338"],
|
||||||
|
opacity: 1,
|
||||||
|
type: 'solid'
|
||||||
|
},
|
||||||
|
stroke: {
|
||||||
|
show: true,
|
||||||
|
curve: 'smooth',
|
||||||
|
lineCap: 'butt',
|
||||||
|
colors: ["#3aa82d"],
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: "Series 1",
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
x: "02-10-2017 GMT",
|
||||||
|
y: 34
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: "02-11-2017 GMT",
|
||||||
|
y: 43
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: "02-12-2017 GMT",
|
||||||
|
y: 31
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: "02-13-2017 GMT",
|
||||||
|
y: 43
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: "02-14-2017 GMT",
|
||||||
|
y: 33
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: "02-15-2017 GMT",
|
||||||
|
y: 52
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
xaxis: {
|
||||||
|
type: "datetime",
|
||||||
|
...axisOptions
|
||||||
|
},
|
||||||
|
yaxis: {
|
||||||
|
...axisOptions
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
{{ range .Services }}
|
{{ range .Services }}
|
||||||
var ctx_{{js .Id}} = document.getElementById("service_{{js .Id}}").getContext('2d');
|
|
||||||
var chartdata_{{js .Id}} = new Chart(ctx_{{js .Id}}, {
|
let chart{{.Id}} = new ApexCharts(document.querySelector("#service_{{js .Id}}"), options);
|
||||||
type: 'line',
|
|
||||||
data: {
|
|
||||||
datasets: [{
|
|
||||||
label: 'Response Time (Milliseconds)',
|
|
||||||
data: [],
|
|
||||||
backgroundColor: ['{{if .Online}}rgba(47, 206, 30, 0.92){{else}}rgb(221, 53, 69){{end}}'],
|
|
||||||
borderColor: ['{{if .Online}}rgb(47, 171, 34){{else}}rgb(183, 32, 47){{end}}'],
|
|
||||||
borderWidth: 1
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
scaleShowValues: false,
|
|
||||||
layout: {
|
|
||||||
padding: {
|
|
||||||
left: 0,
|
|
||||||
right: 0,
|
|
||||||
top: 0,
|
|
||||||
bottom: -10
|
|
||||||
}
|
|
||||||
},
|
|
||||||
hover: {
|
|
||||||
animationDuration: 0,
|
|
||||||
},
|
|
||||||
responsiveAnimationDuration: 0,
|
|
||||||
animation: {
|
|
||||||
duration: 3500,
|
|
||||||
onComplete: onChartComplete
|
|
||||||
},
|
|
||||||
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,
|
|
||||||
time: {
|
|
||||||
displayFormats: {
|
|
||||||
'hour': 'MMM DD hA'
|
|
||||||
},
|
|
||||||
source: 'auto'
|
|
||||||
},
|
|
||||||
gridLines: {
|
|
||||||
display: false
|
|
||||||
},
|
|
||||||
ticks: {
|
|
||||||
source: 'auto',
|
|
||||||
stepSize: 1,
|
|
||||||
min: 0,
|
|
||||||
fontColor: "white",
|
|
||||||
fontSize: 20,
|
|
||||||
display: false
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
elements: {
|
|
||||||
point: {
|
|
||||||
radius: 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
function onChartComplete(chart) {
|
function onChartComplete(chart) {
|
||||||
|
@ -140,7 +193,7 @@ function onChartComplete(chart) {
|
||||||
}
|
}
|
||||||
|
|
||||||
$( document ).ready(function() {
|
$( document ).ready(function() {
|
||||||
{{ range .Services }}
|
{{ range .Services }}AjaxChart(chart{{js .Id}}, {{js .Id}}, 0, 9999999999);
|
||||||
AjaxChart(chartdata_{{js .Id}},{{js .Id}},{{$start}},9999999999,"hour");{{end}}
|
{{end}}
|
||||||
});
|
});
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
@ -124,11 +124,10 @@ function AjaxChart(chart, service, start=0, end=9999999999, group="hour", retry=
|
||||||
} else if (data.data.length === 0) {
|
} else if (data.data.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
chart.data.labels.pop();
|
chart.render();
|
||||||
data.data.forEach(function(d) {
|
chart.updateSeries([{
|
||||||
chart.data.datasets[0].data.push(d);
|
data: data.data
|
||||||
});
|
}]);
|
||||||
chart.update();
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -136,6 +136,7 @@ HTML,BODY {
|
||||||
position: relative;
|
position: relative;
|
||||||
height: 170px;
|
height: 170px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.service-chart-container {
|
.service-chart-container {
|
||||||
|
|
|
@ -63,7 +63,7 @@
|
||||||
<span class="badge bg-danger float-right pulse">OFFLINE</span>
|
<span class="badge bg-danger float-right pulse">OFFLINE</span>
|
||||||
{{end}}</h4>
|
{{end}}</h4>
|
||||||
|
|
||||||
<div class="row stats_area mt-5 mb-5">
|
<div class="row stats_area mt-5">
|
||||||
<div class="col-4">
|
<div class="col-4">
|
||||||
<span class="lg_number">{{.AvgTime}}ms</span>
|
<span class="lg_number">{{.AvgTime}}ms</span>
|
||||||
Average Response
|
Average Response
|
||||||
|
@ -82,7 +82,7 @@
|
||||||
</div>
|
</div>
|
||||||
{{ if .AvgUptime24 }}
|
{{ if .AvgUptime24 }}
|
||||||
<div class="chart-container">
|
<div class="chart-container">
|
||||||
<canvas id="service_{{ .Id }}"></canvas>
|
<div id="service_{{ .Id }}"></div>
|
||||||
</div>
|
</div>
|
||||||
{{ end }}
|
{{ end }}
|
||||||
<div class="row lower_canvas full-col-12 text-white{{if not .Online}} bg-danger{{end}}">
|
<div class="row lower_canvas full-col-12 text-white{{if not .Online}} bg-danger{{end}}">
|
||||||
|
|
|
@ -2,12 +2,12 @@
|
||||||
{{if USE_CDN}}
|
{{if USE_CDN}}
|
||||||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
|
||||||
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js" integrity="sha384-smHYKdLADwkXOn1EmN1qk/HfnUcbVRZyYmZ4qpPea6sjB/pTJ0euyQp0Mk8ck+5T" crossorigin="anonymous"></script>
|
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js" integrity="sha384-smHYKdLADwkXOn1EmN1qk/HfnUcbVRZyYmZ4qpPea6sjB/pTJ0euyQp0Mk8ck+5T" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.7.2/Chart.bundle.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/apexcharts"></script>
|
||||||
<script src="https://assets.statping.com/main.js"></script>
|
<script src="https://assets.statping.com/main.js"></script>
|
||||||
{{ else }}
|
{{ else }}
|
||||||
<script src="/js/jquery-3.3.1.min.js"></script>
|
<script src="/js/jquery-3.3.1.min.js"></script>
|
||||||
<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/apexcharts.min.js"></script>
|
||||||
<script src="/js/main.js"></script>
|
<script src="/js/main.js"></script>
|
||||||
{{end}}
|
{{end}}
|
||||||
{{block "extra_scripts" .}} {{end}}
|
{{block "extra_scripts" .}} {{end}}
|
||||||
|
|
|
@ -59,7 +59,11 @@
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
||||||
<div class="service-chart-container">
|
<div class="service-chart-container">
|
||||||
<canvas id="service"></canvas>
|
<div id="service"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col-12">
|
||||||
|
<div id="service_heatmap"></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form id="service_date_form" class="col-12 mt-2 mb-3">
|
<form id="service_date_form" class="col-12 mt-2 mb-3">
|
||||||
|
@ -205,69 +209,79 @@
|
||||||
<script>
|
<script>
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
|
|
||||||
var ctx = document.getElementById("service").getContext('2d');
|
|
||||||
|
|
||||||
var chartdata = new Chart(ctx, {
|
let options = {
|
||||||
type: 'line',
|
chart: {
|
||||||
data: {
|
height: "100%",
|
||||||
datasets: [{
|
width: "100%",
|
||||||
label: 'Response Time (Milliseconds)',
|
type: "area",
|
||||||
data: [],
|
animations: {
|
||||||
backgroundColor: [
|
enabled: false,
|
||||||
'rgba(47, 206, 30, 0.92)'
|
initialAnimation: {
|
||||||
],
|
enabled: false
|
||||||
borderColor: [
|
|
||||||
'rgb(47, 171, 34)'
|
|
||||||
],
|
|
||||||
borderWidth: 1
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
legend: {
|
|
||||||
display: false
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
yAxes: [{
|
|
||||||
ticks: {
|
|
||||||
beginAtZero: true
|
|
||||||
},
|
|
||||||
gridLines: {
|
|
||||||
display: true
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
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: {
|
|
||||||
radius: 0
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fill: {
|
||||||
|
colors: ["#48d338"],
|
||||||
|
opacity: 1,
|
||||||
|
type: 'solid'
|
||||||
|
},
|
||||||
|
stroke: {
|
||||||
|
show: true,
|
||||||
|
curve: 'smooth',
|
||||||
|
lineCap: 'butt',
|
||||||
|
colors: ["#3aa82d"],
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: "Response Time",
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
x: "02-10-2017 GMT",
|
||||||
|
y: 34
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: "02-11-2017 GMT",
|
||||||
|
y: 43
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: "02-12-2017 GMT",
|
||||||
|
y: 31
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: "02-13-2017 GMT",
|
||||||
|
y: 43
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: "02-14-2017 GMT",
|
||||||
|
y: 33
|
||||||
|
},
|
||||||
|
{
|
||||||
|
x: "02-15-2017 GMT",
|
||||||
|
y: 52
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
],
|
||||||
});
|
xaxis: {
|
||||||
|
type: "datetime",
|
||||||
|
},
|
||||||
|
yaxis: {
|
||||||
|
labels: {
|
||||||
|
formatter: (value) => {
|
||||||
|
return (value * 0.1).toFixed(0) + "ms"
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
dataLabels: {
|
||||||
|
enabled: false
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
AjaxChart(chartdata,{{$s.Id}},{{.StartUnix}},{{.EndUnix}},"hour");
|
let chart = new ApexCharts(document.querySelector("#service"), options);
|
||||||
|
|
||||||
|
AjaxChart(chart,{{$s.Id}},{{.StartUnix}},{{.EndUnix}},"hour");
|
||||||
|
|
||||||
let startDate = $("#service_start").flatpickr({
|
let startDate = $("#service_start").flatpickr({
|
||||||
enableTime: false,
|
enableTime: false,
|
||||||
|
@ -290,6 +304,47 @@ $(document).ready(function() {
|
||||||
startDate.open()
|
startDate.open()
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var heat_options = {
|
||||||
|
chart: {
|
||||||
|
height: "100%",
|
||||||
|
width: "100%",
|
||||||
|
type: 'heatmap',
|
||||||
|
toolbar: {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dataLabels: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
enableShades: true,
|
||||||
|
shadeIntensity: 0.5,
|
||||||
|
colors: ["#d53a3b"],
|
||||||
|
series: [{}],
|
||||||
|
};
|
||||||
|
|
||||||
|
var heatChart = new ApexCharts(
|
||||||
|
document.querySelector("#service_heatmap"),
|
||||||
|
heat_options
|
||||||
|
);
|
||||||
|
|
||||||
|
var heatmapData = [];
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
url: "/api/services/{{$s.Id}}/heatmap",
|
||||||
|
type: 'GET',
|
||||||
|
success: function(data) {
|
||||||
|
data.forEach(function(d) {
|
||||||
|
var date = new Date(d.date);
|
||||||
|
heatmapData.push({name: date.toLocaleString('en-us', { month: 'long' }), data: d.data});
|
||||||
|
});
|
||||||
|
heatChart.render();
|
||||||
|
heatChart.updateSeries(heatmapData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
{{end}}
|
{{end}}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
// Code generated by go generate; DO NOT EDIT.
|
// Code generated by go generate; DO NOT EDIT.
|
||||||
// This file was generated by robots at
|
// This file was generated by robots at
|
||||||
// 2019-01-17 01:23:49.15263 -0800 PST m=+0.644968431
|
// 2019-01-29 04:01:15.747029 -0800 PST m=+0.799396747
|
||||||
//
|
//
|
||||||
// This contains the most recently Markdown source for the Statping Wiki.
|
// This contains the most recently Markdown source for the Statping Wiki.
|
||||||
package source
|
package source
|
||||||
|
|
|
@ -26,6 +26,7 @@ type Failure struct {
|
||||||
Issue string `gorm:"column:issue" json:"issue"`
|
Issue string `gorm:"column:issue" json:"issue"`
|
||||||
Method string `gorm:"column:method" json:"method,omitempty"`
|
Method string `gorm:"column:method" json:"method,omitempty"`
|
||||||
MethodId int64 `gorm:"column:method_id" json:"method_id,omitempty"`
|
MethodId int64 `gorm:"column:method_id" json:"method_id,omitempty"`
|
||||||
|
ErrorCode int `gorm:"column:error_code" json:"error_code"`
|
||||||
Service int64 `gorm:"index;column:service" json:"-"`
|
Service int64 `gorm:"index;column:service" json:"-"`
|
||||||
Checkin int64 `gorm:"index;column:checkin" json:"-"`
|
Checkin int64 `gorm:"index;column:checkin" json:"-"`
|
||||||
PingTime float64 `gorm:"column:ping_time" json:"ping"`
|
PingTime float64 `gorm:"column:ping_time" json:"ping"`
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
0.80.39
|
0.80.40
|
||||||
|
|
Loading…
Reference in New Issue