Show 90-day uptime bars on the status page
Every check is also counted in a per-day table that outlives the raw checks, so the public page can draw one bar per UTC day with a tooltip, as most hosted status pages do. Existing databases are backfilled once from the checks they still have. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
73f45fb3ae
commit
4ea4fe5289
7 files changed
+221
-14
No files matched your search
@@ -233,6 +233,37 @@ func (s *Server) monitorState(id string) MonitorState {
|
||||
return st
|
||||
}
|
||||
|
||||
// uptimeDays is how many days of bars the status page shows.
|
||||
const uptimeDays = 90
|
||||
|
||||
// dailyUptime returns [checks, failed] for each of the last n UTC days, oldest first, so the
|
||||
// last entry is today. A day with no checks is [0, 0].
|
||||
func (s *Server) dailyUptime(monitorID string, n int, now time.Time) [][2]int {
|
||||
out := make([][2]int, n)
|
||||
today := now.UTC().Truncate(24 * time.Hour)
|
||||
rows, err := s.st.db.Query(`SELECT day,checks,fails FROM daily WHERE monitor_id=? AND day>=?`,
|
||||
monitorID, today.AddDate(0, 0, -(n-1)).Format("2006-01-02"))
|
||||
if err != nil {
|
||||
return out
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var day string
|
||||
var c, f int
|
||||
if rows.Scan(&day, &c, &f) != nil {
|
||||
continue
|
||||
}
|
||||
d, err := time.Parse("2006-01-02", day)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if i := n - 1 - int(today.Sub(d).Hours()/24); i >= 0 && i < n {
|
||||
out[i] = [2]int{c, f}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func (s *Server) saveState(st MonitorState) {
|
||||
s.st.db.Exec(`INSERT INTO mstate(monitor_id,status,since,last_check,last_ms,fails,last_ping,detail) VALUES(?,?,?,?,?,?,?,?)
|
||||
ON CONFLICT(monitor_id) DO UPDATE SET status=excluded.status, since=excluded.since, last_check=excluded.last_check,
|
||||
@@ -309,6 +340,8 @@ func (s *Server) recordResult(m *Monitor, st MonitorState, now time.Time, dur ti
|
||||
okInt = 1
|
||||
}
|
||||
s.st.db.Exec(`INSERT INTO checks(monitor_id,time,ok,ms,detail) VALUES(?,?,?,?,?)`, m.ID, now.Unix(), okInt, dur.Milliseconds(), errText(err))
|
||||
s.st.db.Exec(`INSERT INTO daily(monitor_id,day,checks,fails) VALUES(?,?,1,?)
|
||||
ON CONFLICT(monitor_id,day) DO UPDATE SET checks=checks+1, fails=fails+excluded.fails`, m.ID, now.UTC().Format("2006-01-02"), 1-okInt)
|
||||
st.LastCheck, st.LastMS, st.Detail = now.Unix(), int(dur.Milliseconds()), errText(err)
|
||||
key := "monitor:" + m.ID
|
||||
if ok {
|
||||
@@ -369,6 +402,7 @@ func (s *Server) Heartbeat(token string) bool {
|
||||
func (s *Server) deleteMonitorData(id string) {
|
||||
s.st.db.Exec(`DELETE FROM mstate WHERE monitor_id=?`, id)
|
||||
s.st.db.Exec(`DELETE FROM checks WHERE monitor_id=?`, id)
|
||||
s.st.db.Exec(`DELETE FROM daily WHERE monitor_id=?`, id)
|
||||
s.ResolveAlertKey("monitor:"+id, "monitor deleted")
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user