mirror of https://github.com/statping/statping
214 lines
5.4 KiB
Go
214 lines
5.4 KiB
Go
package database
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/hunterlong/statping/types"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type ServiceObj struct {
|
|
*types.Service
|
|
o *Object
|
|
|
|
Servicer
|
|
}
|
|
|
|
type Servicer interface {
|
|
Failures() *FailureObj
|
|
Checkins() []*CheckinObj
|
|
DowntimeText() string
|
|
UpdateStats()
|
|
Model() *ServiceObj
|
|
|
|
Hittable
|
|
}
|
|
|
|
type Hittable interface {
|
|
Hits() *HitObj
|
|
CreateHit(hit *types.Hit) *HitObj
|
|
}
|
|
|
|
func (s *ServiceObj) Model() *ServiceObj {
|
|
return s
|
|
}
|
|
|
|
func Service(id int64) (*ServiceObj, error) {
|
|
var service types.Service
|
|
query := database.Services().Where("id = ?", id)
|
|
finer := query.Find(&service)
|
|
return &ServiceObj{Service: &service, o: wrapObject(id, &service, query)}, finer.Error()
|
|
}
|
|
|
|
func wrapServices(all []*types.Service, db Database) []*ServiceObj {
|
|
var arr []*ServiceObj
|
|
for _, v := range all {
|
|
arr = append(arr, &ServiceObj{Service: v, o: wrapObject(v.Id, v, db)})
|
|
}
|
|
return arr
|
|
}
|
|
|
|
func Services() []*ServiceObj {
|
|
var services []*types.Service
|
|
db := database.Services().Order("order_id desc")
|
|
db.Find(&services)
|
|
return wrapServices(services, db)
|
|
}
|
|
|
|
func (s *ServiceObj) Checkins() []*CheckinObj {
|
|
var checkins []*types.Checkin
|
|
query := database.Checkins().Where("service = ?", s.Id)
|
|
query.Find(&checkins)
|
|
return wrapCheckins(checkins, query)
|
|
}
|
|
|
|
func (s *ServiceObj) DowntimeText() string {
|
|
last := s.Failures().Last(1)
|
|
if len(last) == 0 {
|
|
return ""
|
|
}
|
|
return parseError(last[0])
|
|
}
|
|
|
|
// ParseError returns a human readable error for a Failure
|
|
func parseError(f *types.Failure) string {
|
|
if f.Method == "checkin" {
|
|
return fmt.Sprintf("Checkin is Offline")
|
|
}
|
|
err := strings.Contains(f.Issue, "connection reset by peer")
|
|
if err {
|
|
return fmt.Sprintf("Connection Reset")
|
|
}
|
|
err = strings.Contains(f.Issue, "operation timed out")
|
|
if err {
|
|
return fmt.Sprintf("HTTP Request Timed Out")
|
|
}
|
|
err = strings.Contains(f.Issue, "x509: certificate is valid")
|
|
if err {
|
|
return fmt.Sprintf("SSL Certificate invalid")
|
|
}
|
|
err = strings.Contains(f.Issue, "Client.Timeout exceeded while awaiting headers")
|
|
if err {
|
|
return fmt.Sprintf("Connection Timed Out")
|
|
}
|
|
err = strings.Contains(f.Issue, "no such host")
|
|
if err {
|
|
return fmt.Sprintf("Domain is offline or not found")
|
|
}
|
|
err = strings.Contains(f.Issue, "HTTP Status Code")
|
|
if err {
|
|
return fmt.Sprintf("Incorrect HTTP Status Code")
|
|
}
|
|
err = strings.Contains(f.Issue, "connection refused")
|
|
if err {
|
|
return fmt.Sprintf("Connection Failed")
|
|
}
|
|
err = strings.Contains(f.Issue, "can't assign requested address")
|
|
if err {
|
|
return fmt.Sprintf("Unable to Request Address")
|
|
}
|
|
err = strings.Contains(f.Issue, "no route to host")
|
|
if err {
|
|
return fmt.Sprintf("Domain is offline or not found")
|
|
}
|
|
err = strings.Contains(f.Issue, "i/o timeout")
|
|
if err {
|
|
return fmt.Sprintf("Connection Timed Out")
|
|
}
|
|
err = strings.Contains(f.Issue, "Client.Timeout exceeded while reading body")
|
|
if err {
|
|
return fmt.Sprintf("Timed Out on Response Body")
|
|
}
|
|
return f.Issue
|
|
}
|
|
|
|
func (s *ServiceObj) Hits() *HitObj {
|
|
query := database.Hits().Where("service = ?", s.Id)
|
|
return &HitObj{wrapObject(s.Id, nil, query)}
|
|
}
|
|
|
|
func (s *ServiceObj) Failures() *FailureObj {
|
|
q := database.Failures().Where("method != 'checkin' AND service = ?", s.Id)
|
|
return &FailureObj{wrapObject(s.Id, nil, q)}
|
|
}
|
|
|
|
func (s *ServiceObj) Group() *GroupObj {
|
|
var group types.Group
|
|
q := database.Groups().Where("id = ?", s.GroupId)
|
|
finder := q.Find(&group)
|
|
if finder.Error() != nil {
|
|
return nil
|
|
}
|
|
return &GroupObj{Group: &group, o: wrapObject(group.Id, &group, q)}
|
|
}
|
|
|
|
func (s *ServiceObj) object() *Object {
|
|
return s.o
|
|
}
|
|
|
|
func (s *ServiceObj) UpdateStats() {
|
|
s.Online24Hours = s.OnlineDaysPercent(1)
|
|
s.Online7Days = s.OnlineDaysPercent(7)
|
|
s.AvgResponse = s.AvgTime()
|
|
s.FailuresLast24Hours = len(s.Failures().Since(time.Now().UTC().Add(-time.Hour * 24)))
|
|
s.Stats = &types.Stats{
|
|
Failures: s.Failures().Count(),
|
|
Hits: s.Hits().Count(),
|
|
}
|
|
}
|
|
|
|
// AvgTime will return the average amount of time for a service to response back successfully
|
|
func (s *ServiceObj) AvgTime() float64 {
|
|
sum := s.Hits().Sum()
|
|
return sum
|
|
}
|
|
|
|
// AvgUptime will return the average amount of time for a service to response back successfully
|
|
func (s *ServiceObj) AvgUptime(since time.Time) float64 {
|
|
sum := s.Hits().Sum()
|
|
return sum
|
|
}
|
|
|
|
// OnlineDaysPercent returns the service's uptime percent within last 24 hours
|
|
func (s *ServiceObj) OnlineDaysPercent(days int) float32 {
|
|
ago := time.Now().UTC().Add((-24 * time.Duration(days)) * time.Hour)
|
|
return s.OnlineSince(ago)
|
|
}
|
|
|
|
// OnlineSince accepts a time since parameter to return the percent of a service's uptime.
|
|
func (s *ServiceObj) OnlineSince(ago time.Time) float32 {
|
|
failed := s.Failures().Since(ago)
|
|
if len(failed) == 0 {
|
|
s.Online24Hours = 100.00
|
|
return s.Online24Hours
|
|
}
|
|
total := s.Hits().Since(ago)
|
|
if len(total) == 0 {
|
|
s.Online24Hours = 0
|
|
return s.Online24Hours
|
|
}
|
|
avg := float64(len(failed)) / float64(len(total)) * 100
|
|
avg = 100 - avg
|
|
if avg < 0 {
|
|
avg = 0
|
|
}
|
|
amount, _ := strconv.ParseFloat(fmt.Sprintf("%0.2f", avg), 10)
|
|
s.Online24Hours = float32(amount)
|
|
return s.Online24Hours
|
|
}
|
|
|
|
// Downtime returns the amount of time of a offline service
|
|
func (s *ServiceObj) Downtime() time.Duration {
|
|
hits := s.Hits().Last(1)
|
|
fail := s.Failures().Last(1)
|
|
if len(fail) == 0 {
|
|
return time.Duration(0)
|
|
}
|
|
if len(fail) == 0 {
|
|
return time.Now().UTC().Sub(fail[0].CreatedAt.UTC())
|
|
}
|
|
since := fail[0].CreatedAt.UTC().Sub(hits[0].CreatedAt.UTC())
|
|
return since
|
|
}
|