diff --git a/core/checker.go b/core/checker.go index 3e4b8b7b..4150913e 100644 --- a/core/checker.go +++ b/core/checker.go @@ -74,20 +74,34 @@ func (s *Service) duration() time.Duration { return amount } +func (s *Service) parseHost() string { + if s.Type == "tcp" { + return s.Domain + } else { + domain := s.Domain + hasPort, _ := regexp.MatchString(`\:([0-9]+)`, domain) + if hasPort { + splitDomain := strings.Split(s.Domain, ":") + domain = splitDomain[len(splitDomain)-2] + } + host, err := url.Parse(domain) + if err != nil { + return s.Domain + } + return host.Host + } +} + // dnsCheck will check the domain name and return a float64 for the amount of time the DNS check took func (s *Service) dnsCheck() (float64, error) { + var err error t1 := time.Now() - domain := s.Domain - hasPort, _ := regexp.MatchString(`\:([0-9]+)`, domain) - if hasPort { - splitDomain := strings.Split(s.Domain, ":") - domain = splitDomain[len(splitDomain)-2] + host := s.parseHost() + if s.Type == "tcp" { + _, err = net.LookupHost(host) + } else { + _, err = net.LookupIP(host) } - url, err := url.Parse(domain) - if err != nil { - return 0, err - } - _, err = net.LookupIP(url.Host) if err != nil { return 0, err } @@ -98,6 +112,14 @@ func (s *Service) dnsCheck() (float64, error) { // checkTcp will check a TCP service func (s *Service) checkTcp(record bool) *Service { + dnsLookup, err := s.dnsCheck() + if err != nil { + if record { + recordFailure(s, fmt.Sprintf("Could not get IP address for TCP service %v, %v", s.Domain, err)) + } + return s + } + s.PingTime = dnsLookup t1 := time.Now() domain := fmt.Sprintf("%v", s.Domain) if s.Port != 0 { @@ -134,7 +156,7 @@ func (s *Service) checkHttp(record bool) *Service { } return s } - s.DnsLookup = dnsLookup + s.PingTime = dnsLookup t1 := time.Now() timeout := time.Duration(s.Timeout) client := http.Client{ @@ -213,9 +235,10 @@ func recordSuccess(s *Service) { hit := &types.Hit{ Service: s.Id, Latency: s.Latency, + PingTime: s.PingTime, CreatedAt: time.Now(), } - utils.Log(1, fmt.Sprintf("Service %v Successful: %0.2f ms", s.Name, hit.Latency*1000)) + utils.Log(1, fmt.Sprintf("Service %v Successful Response: %0.2f ms | Lookup in: %0.2f ms", s.Name, hit.Latency*1000, hit.PingTime*1000)) s.CreateHit(hit) notifier.OnSuccess(s.Service) } @@ -226,9 +249,10 @@ func recordFailure(s *Service, issue string) { fail := &types.Failure{ Service: s.Id, Issue: issue, + PingTime: s.PingTime, CreatedAt: time.Now(), } - utils.Log(2, fmt.Sprintf("Service %v Failing: %v", s.Name, issue)) + utils.Log(2, fmt.Sprintf("Service %v Failing: %v | Lookup in: %0.2f ms", s.Name, issue, fail.PingTime*1000)) s.CreateFailure(fail) notifier.OnFailure(s.Service, fail) } diff --git a/core/database.go b/core/database.go index a2d7c73d..02603d75 100644 --- a/core/database.go +++ b/core/database.go @@ -67,8 +67,8 @@ 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, group string) *gorm.DB { - selector := Dbtimestamp(group) +func (s *Service) HitsBetween(t1, t2 time.Time, group string, column string) *gorm.DB { + selector := Dbtimestamp(group, column) return DbSession.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") } @@ -291,7 +291,7 @@ func (db *DbConfig) DropDatabase() error { func (db *DbConfig) CreateDatabase() error { utils.Log(1, "Creating Database Tables...") err := DbSession.CreateTable(&types.Checkin{}) - err = DbSession.CreateTable(&types.CheckinHit{}) + //err = DbSession.CreateTable(&types.CheckinHit{}) err = DbSession.CreateTable(¬ifier.Notification{}) err = DbSession.Table("core").CreateTable(&types.Core{}) err = DbSession.CreateTable(&types.Failure{}) @@ -317,7 +317,7 @@ func (db *DbConfig) MigrateDatabase() error { if tx.Error != nil { return tx.Error } - tx = tx.AutoMigrate(&types.Service{}, &types.User{}, &types.Hit{}, &types.Failure{}, &types.Checkin{}, &types.CheckinHit{}, ¬ifier.Notification{}).Table("core").AutoMigrate(&types.Core{}) + tx = tx.AutoMigrate(&types.Service{}, &types.User{}, &types.Hit{}, &types.Failure{}, &types.Checkin{}, ¬ifier.Notification{}).Table("core").AutoMigrate(&types.Core{}) if tx.Error != nil { tx.Rollback() utils.Log(3, fmt.Sprintf("Statup Database could not be migrated: %v", tx.Error)) diff --git a/core/services.go b/core/services.go index 58bc9035..d8b899b9 100644 --- a/core/services.go +++ b/core/services.go @@ -172,7 +172,7 @@ func (s *Service) DowntimeText() string { return fmt.Sprintf("%v has been offline for %v", s.Name, utils.DurationReadable(s.Downtime())) } -func Dbtimestamp(group string) string { +func Dbtimestamp(group string, column string) string { seconds := 60 if group == "second" { seconds = 60 @@ -183,11 +183,11 @@ func Dbtimestamp(group string) string { } switch CoreApp.DbConnection { case "mysql": - return fmt.Sprintf("CONCAT(date_format(created_at, '%%Y-%%m-%%d %%H:00:00')) AS timeframe, AVG(latency) AS value") + return fmt.Sprintf("CONCAT(date_format(created_at, '%%Y-%%m-%%d %%H:00:00')) AS timeframe, AVG(%v) AS value", column) case "sqlite": - return fmt.Sprintf("datetime((strftime('%%s', created_at) / %v) * %v, 'unixepoch') AS timeframe, AVG(latency) as value", seconds, seconds) + return fmt.Sprintf("datetime((strftime('%%s', created_at) / %v) * %v, 'unixepoch') AS timeframe, AVG(%v) as value", seconds, seconds, column) case "postgres": - return fmt.Sprintf("date_trunc('%v', created_at) AS timeframe, AVG(latency) AS value", group) + return fmt.Sprintf("date_trunc('%v', created_at) AS timeframe, AVG(%v) AS value", group, column) default: return "" } @@ -207,9 +207,9 @@ func (s *Service) Downtime() time.Duration { return since } -func GraphDataRaw(service types.ServiceInterface, start, end time.Time, group string) *DateScanObj { +func GraphDataRaw(service types.ServiceInterface, start, end time.Time, group string, column string) *DateScanObj { var d []DateScan - model := service.(*Service).HitsBetween(start, end, group) + model := service.(*Service).HitsBetween(start, end, group, column) rows, _ := model.Rows() for rows.Next() { var gd DateScan @@ -241,7 +241,7 @@ func (d *DateScanObj) ToString() string { func (s *Service) GraphData() string { start := time.Now().Add((-24 * 7) * time.Hour) end := time.Now() - obj := GraphDataRaw(s, start, end, "hour") + obj := GraphDataRaw(s, start, end, "hour", "latency") data, err := json.Marshal(obj) if err != nil { utils.Log(2, err) diff --git a/core/services_test.go b/core/services_test.go index f73a92fe..6f9704db 100644 --- a/core/services_test.go +++ b/core/services_test.go @@ -100,6 +100,7 @@ func TestCheckHTTPService(t *testing.T) { assert.True(t, service.Online) assert.Equal(t, 200, service.LastStatusCode) assert.NotZero(t, service.Latency) + assert.NotZero(t, service.PingTime) } func TestServiceTCPCheck(t *testing.T) { @@ -114,6 +115,7 @@ func TestCheckTCPService(t *testing.T) { assert.Equal(t, "Changed Google DNS", service.Name) assert.True(t, service.Online) assert.NotZero(t, service.Latency) + assert.NotZero(t, service.PingTime) } func TestServiceOnline24Hours(t *testing.T) { diff --git a/handlers/api.go b/handlers/api.go index 109fe311..760cd01f 100644 --- a/handlers/api.go +++ b/handlers/api.go @@ -89,7 +89,24 @@ func apiServiceDataHandler(w http.ResponseWriter, r *http.Request) { 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) + obj := core.GraphDataRaw(service, time.Unix(startField, 0), time.Unix(endField, 0), grouping, "latency") + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(obj) +} + +func apiServicePingDataHandler(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, "ping_time") w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(obj) diff --git a/handlers/handlers.go b/handlers/handlers.go index b56fbc45..92a30832 100644 --- a/handlers/handlers.go +++ b/handlers/handlers.go @@ -164,7 +164,7 @@ func executeResponse(w http.ResponseWriter, r *http.Request, file string, data i templates := []string{"base.html", "head.html", "nav.html", "footer.html", "scripts.html", "form_service.html", "form_notifier.html", "form_user.html"} - javascripts := []string{"chart_index.js"} + javascripts := []string{"charts.js", "chart_index.js"} render, err := source.TmplBox.String(file) if err != nil { @@ -221,9 +221,19 @@ func executeJSResponse(w http.ResponseWriter, r *http.Request, file string, data "safe": func(html string) template.HTML { return template.HTML(html) }, + "Services": func() []types.ServiceInterface { + return core.CoreApp.Services + }, }) - t.Parse(render) - t.Execute(w, data) + _, err = t.Parse(render) + if err != nil { + utils.Log(4, err) + } + + err = t.Execute(w, data) + if err != nil { + utils.Log(4, err) + } } // error404Handler is a HTTP handler for 404 error pages diff --git a/handlers/routes.go b/handlers/routes.go index 6231c3c4..58aeb820 100644 --- a/handlers/routes.go +++ b/handlers/routes.go @@ -47,7 +47,7 @@ func Router() *mux.Router { r.PathPrefix("/statup.png").Handler(http.FileServer(source.TmplBox.HTTPBox())) } r.PathPrefix("/js/").Handler(http.StripPrefix("/js/", http.FileServer(source.JsBox.HTTPBox()))) - r.Handle("/charts/{id}.js", http.HandlerFunc(renderServiceChartHandler)) + //r.Handle("/charts/{id}.js", http.HandlerFunc(renderServiceChartHandler)) r.Handle("/charts.js", http.HandlerFunc(renderServiceChartsHandler)) r.Handle("/setup", http.HandlerFunc(setupHandler)).Methods("GET") r.Handle("/setup", http.HandlerFunc(processSetupHandler)).Methods("POST") @@ -87,6 +87,7 @@ func Router() *mux.Router { 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}/ping", http.HandlerFunc(apiServicePingDataHandler)).Methods("GET") r.Handle("/api/services/{id}", http.HandlerFunc(apiServiceUpdateHandler)).Methods("POST") r.Handle("/api/services/{id}", http.HandlerFunc(apiServiceDeleteHandler)).Methods("DELETE") diff --git a/handlers/services.go b/handlers/services.go index d2b7383f..bde5bddf 100644 --- a/handlers/services.go +++ b/handlers/services.go @@ -54,14 +54,14 @@ func renderServiceChartHandler(w http.ResponseWriter, r *http.Request) { } service := core.SelectService(utils.StringInt(vars["id"])) - data := core.GraphDataRaw(service, start, end, "hour").ToString() + data := core.GraphDataRaw(service, start, end, "hour", "latency").ToString() out := struct { Services []*core.Service Data []string }{[]*core.Service{service}, []string{data}} - executeJSResponse(w, r, "charts.js", out) + executeResponse(w, r, "charts.js", out, nil) } func renderServiceChartsHandler(w http.ResponseWriter, r *http.Request) { @@ -69,19 +69,16 @@ func renderServiceChartsHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/javascript") w.Header().Set("Cache-Control", "max-age=60") - var data []string - end := now.EndOfDay().UTC() - start := now.BeginningOfDay().UTC() - + //var data []string + //end := now.EndOfDay().UTC() + //start := now.BeginningOfDay().UTC() + var srvs []*core.Service for _, s := range services { - d := core.GraphDataRaw(s, start, end, "hour").ToString() - data = append(data, d) + srvs = append(srvs, s.(*core.Service)) } - out := struct { - Services []types.ServiceInterface - Data []string - }{services, data} + Services []*core.Service + }{srvs} executeJSResponse(w, r, "charts.js", out) } @@ -194,7 +191,7 @@ func servicesViewHandler(w http.ResponseWriter, r *http.Request) { end = time.Unix(endField, 0) } - data := core.GraphDataRaw(serv, start, end, "hour") + data := core.GraphDataRaw(serv, start, end, "hour", "latency") out := struct { Service *core.Service diff --git a/source/js/chart_index.js b/source/js/chart_index.js index d7ae8f71..bb7ac455 100644 --- a/source/js/chart_index.js +++ b/source/js/chart_index.js @@ -6,8 +6,8 @@ var chartdata_{{js .Id}} = new Chart(ctx_{{js .Id}}, { datasets: [{ label: 'Response Time (Milliseconds)', data: [], - backgroundColor: ['rgba(47, 206, 30, 0.92)'], - borderColor: ['rgb(47, 171, 34)'], + 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 }] }, @@ -88,7 +88,7 @@ var chartdata_{{js .Id}} = new Chart(ctx_{{js .Id}}, { display: !1 }, tooltips: { - "enabled": !1 + enabled: !1 }, scales: { yAxes: [{ diff --git a/source/js/charts.js b/source/js/charts.js index 3f5c935a..e9d16474 100644 --- a/source/js/charts.js +++ b/source/js/charts.js @@ -1,23 +1,137 @@ -/* - * Statup - * Copyright (C) 2018. Hunter Long and the project contributors - * Written by Hunter Long and the project contributors - * - * https://github.com/hunterlong/statup - * - * The licenses for most software and other practical works are designed - * to take away your freedom to share and change the works. By contrast, - * the GNU General Public License is intended to guarantee your freedom to - * share and change all versions of a program--to make sure it remains free - * software for all its users. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ +{{define "charts"}}{{ range Services }} +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: ['{{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: !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 + } + } + } +}); -{{$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,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 }} +AjaxChart(chartdata_{{js .Id}},{{js .Id}},0,99999999999,"hour"); +{{end}}{{end}} diff --git a/source/js/main.js b/source/js/main.js index b40fc345..03b3b554 100644 --- a/source/js/main.js +++ b/source/js/main.js @@ -95,6 +95,25 @@ function AjaxChart(chart, service, start=0, end=9999999999, group="hour") { }); } +function PingAjaxChart(chart, service, start=0, end=9999999999, group="hour") { + $.ajax({ + url: "/api/services/"+service+"/ping?start="+start+"&end="+end+"&group="+group, + type: 'GET', + success: function(data) { + chart.data.labels.pop(); + chart.data.datasets.push({ + label: "Ping Time", + backgroundColor: "#bababa" + }); + chart.update(); + data.data.forEach(function(d) { + chart.data.datasets[1].data.push(d); + }); + chart.update(); + } + }); +} + $('select#service_check_type').on('change', function() { var selected = $('#service_check_type option:selected').val(); if (selected === 'POST') { diff --git a/source/tmpl/form_service.html b/source/tmpl/form_service.html index 9f3cd15b..c796c305 100644 --- a/source/tmpl/form_service.html +++ b/source/tmpl/form_service.html @@ -1,5 +1,5 @@ {{define "form_service"}} -
+
@@ -49,26 +49,26 @@
- +
- +
- + 10,000+ will be checked in Microseconds (1 millisecond = 1000 microseconds).
- +
@@ -78,12 +78,14 @@
-
- +
+
+ {{if ne .Id 0}} + {{end}}
{{end}} diff --git a/source/tmpl/form_user.html b/source/tmpl/form_user.html index c90fca6a..c233614a 100644 --- a/source/tmpl/form_user.html +++ b/source/tmpl/form_user.html @@ -1,5 +1,5 @@ {{define "form_user"}} -
+
@@ -32,7 +32,7 @@
- +
diff --git a/source/tmpl/index.html b/source/tmpl/index.html index a8ba2649..ac21821c 100644 --- a/source/tmpl/index.html +++ b/source/tmpl/index.html @@ -80,9 +80,5 @@
{{end}} {{define "extra_scripts"}} - + {{end}} diff --git a/types/failure.go b/types/failure.go index bddc44d6..e7301d0d 100644 --- a/types/failure.go +++ b/types/failure.go @@ -26,6 +26,7 @@ type Failure struct { Issue string `gorm:"column:issue" json:"issue"` Method string `gorm:"column:method" json:"method,omitempty"` Service int64 `gorm:"index;column:service" json:"-"` + PingTime float64 `gorm:"column:ping_time"` CreatedAt time.Time `gorm:"column:created_at" json:"created_at"` FailureInterface `gorm:"-" json:"-"` } diff --git a/types/service.go b/types/service.go index 95d071aa..bb6f6610 100644 --- a/types/service.go +++ b/types/service.go @@ -36,6 +36,7 @@ type Service struct { UpdatedAt time.Time `gorm:"column:updated_at" json:"updated_at"` Online bool `gorm:"-" json:"online"` Latency float64 `gorm:"-" json:"latency"` + PingTime float64 `gorm:"-" json:"ping_time"` Online24Hours float32 `gorm:"-" json:"24_hours_online"` AvgResponse string `gorm:"-" json:"avg_response"` Running chan bool `gorm:"-" json:"-"` @@ -44,7 +45,6 @@ type Service struct { LastResponse string `gorm:"-" json:"-"` LastStatusCode int `gorm:"-" json:"status_code"` LastOnline time.Time `gorm:"-" json:"last_online"` - DnsLookup float64 `gorm:"-" json:"dns_lookup_time"` Failures []interface{} `gorm:"-" json:"failures,omitempty"` Checkins []*Checkin `gorm:"-" json:"checkins,omitempty"` }