diff --git a/go.mod b/go.mod index dfb0d0c..af0b081 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/gen2brain/beeep v0.0.0-20230907135156-1a38885a97fc github.com/gizak/termui/v3 v3.1.0 github.com/mattn/go-runewidth v0.0.15 + github.com/caio/go-tdigest/v4 v4.0.1 ) require ( diff --git a/go.sum b/go.sum index 26a6c5a..8e9fba5 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/caio/go-tdigest/v4 v4.0.1 h1:sx4ZxjmIEcLROUPs2j1BGe2WhOtHD6VSe6NNbBdKYh4= +github.com/caio/go-tdigest/v4 v4.0.1/go.mod h1:Wsa+f0EZnV2gShdj1adgl0tQSoXRxtM0QioTgukFw8U= github.com/gen2brain/beeep v0.0.0-20230907135156-1a38885a97fc h1:NNgdMgPX3j33uEAoVVxNxillDPnxT0xbGv8uh4CKIAo= github.com/gen2brain/beeep v0.0.0-20230907135156-1a38885a97fc/go.mod h1:0W7dI87PvXJ1Sjs0QPvWXKcQmNERY77e8l7GFhZB/s4= github.com/gizak/termui/v3 v3.1.0 h1:ZZmVDgwHl7gR7elfKf1xc4IudXZ5qqfDh4wExk4Iajc= diff --git a/images/demo.png b/images/demo.png index fdd7035..2261ca5 100644 Binary files a/images/demo.png and b/images/demo.png differ diff --git a/tui/tui.go b/tui/tui.go index cda0c48..4370e8e 100644 --- a/tui/tui.go +++ b/tui/tui.go @@ -2,12 +2,14 @@ package tui import ( "fmt" + "math" "time" "github.com/Owloops/updo/net" "github.com/Owloops/updo/utils" uw "github.com/Owloops/updo/widgets" + "github.com/caio/go-tdigest/v4" ui "github.com/gizak/termui/v3" "github.com/gizak/termui/v3/widgets" ) @@ -19,11 +21,17 @@ type Manager struct { StartTime time.Time LastCheckTime time.Time IsUp bool + MinResponseTime float64 + MaxResponseTime float64 + TDigest *tdigest.TDigest QuitWidget *widgets.Paragraph UptimeWidget *widgets.Paragraph UpForWidget *widgets.Paragraph AvgResponseTimeWidget *widgets.Paragraph + MinResponseTimeWidget *widgets.Paragraph + MaxResponseTimeWidget *widgets.Paragraph + P95ResponseTimeWidget *widgets.Paragraph SSLOkWidget *widgets.Paragraph UptimePlot *widgets.Plot ResponseTimePlot *widgets.Plot @@ -35,8 +43,14 @@ type Manager struct { } func NewManager() *Manager { + td, err := tdigest.New(tdigest.Compression(100)) + if err != nil { + } return &Manager{ - StartTime: time.Now(), + StartTime: time.Now(), + MinResponseTime: math.MaxFloat64, + MaxResponseTime: 0, + TDigest: td, } } @@ -57,10 +71,25 @@ func (m *Manager) InitializeWidgets(url string, refreshInterval time.Duration) { m.UpForWidget.BorderStyle.Fg = ui.ColorBlue m.AvgResponseTimeWidget = widgets.NewParagraph() - m.AvgResponseTimeWidget.Title = "Average Response Time" + m.AvgResponseTimeWidget.Title = "Average" m.AvgResponseTimeWidget.Text = "N/A" m.AvgResponseTimeWidget.BorderStyle.Fg = ui.ColorCyan + m.MinResponseTimeWidget = widgets.NewParagraph() + m.MinResponseTimeWidget.Title = "Min" + m.MinResponseTimeWidget.Text = "N/A" + m.MinResponseTimeWidget.BorderStyle.Fg = ui.ColorCyan + + m.MaxResponseTimeWidget = widgets.NewParagraph() + m.MaxResponseTimeWidget.Title = "Max" + m.MaxResponseTimeWidget.Text = "N/A" + m.MaxResponseTimeWidget.BorderStyle.Fg = ui.ColorCyan + + m.P95ResponseTimeWidget = widgets.NewParagraph() + m.P95ResponseTimeWidget.Title = "95p" + m.P95ResponseTimeWidget.Text = "N/A" + m.P95ResponseTimeWidget.BorderStyle.Fg = ui.ColorCyan + m.SSLOkWidget = widgets.NewParagraph() m.SSLOkWidget.Title = "SSL Certificate" m.SSLOkWidget.Text = "N/A" @@ -113,17 +142,26 @@ func (m *Manager) InitializeWidgets(url string, refreshInterval time.Duration) { ui.NewCol(1.0/4, m.QuitWidget), ), ui.NewRow(1.0/7, - ui.NewCol(1.0/4, m.UptimeWidget), - ui.NewCol(1.0/4, m.AvgResponseTimeWidget), - ui.NewCol(1.0/4, m.AssertionWidget), - ui.NewCol(1.0/4, m.SSLOkWidget), + ui.NewCol(1.0/3, m.UptimeWidget), + ui.NewCol(1.0/3, m.AssertionWidget), + ui.NewCol(1.0/3, m.SSLOkWidget), ), ui.NewRow(5.0/7, ui.NewCol(3.0/5, ui.NewRow(0.5, m.ResponseTimePlot), ui.NewRow(0.5, m.UptimePlot), ), - ui.NewCol(2.0/5, ui.NewRow(1.0, m.TimingBreakdownWidget)), + ui.NewCol(2.0/5, + ui.NewRow(0.5/2, + ui.NewCol(1.0/2, m.MinResponseTimeWidget), + ui.NewCol(1.0/2, m.MaxResponseTimeWidget), + ), + ui.NewRow(0.5/2, + ui.NewCol(1.0/2, m.AvgResponseTimeWidget), + ui.NewCol(1.0/2, m.P95ResponseTimeWidget), + ), + ui.NewRow(1.0/2, m.TimingBreakdownWidget), + ), ), ) } @@ -141,6 +179,23 @@ func (m *Manager) UpdateWidgets(result net.WebsiteCheckResult, width int, height avgResponseTime := m.TotalResponseTime / time.Duration(m.ChecksCount) m.AvgResponseTimeWidget.Text = utils.FormatDurationMillisecond(avgResponseTime) + if m.ChecksCount == 1 || result.ResponseTime < time.Duration(m.MinResponseTime) { + m.MinResponseTime = float64(result.ResponseTime) + } + if result.ResponseTime > time.Duration(m.MaxResponseTime) { + m.MaxResponseTime = float64(result.ResponseTime) + } + + m.MinResponseTimeWidget.Text = utils.FormatDurationMillisecond(time.Duration(m.MinResponseTime)) + m.MaxResponseTimeWidget.Text = utils.FormatDurationMillisecond(time.Duration(m.MaxResponseTime)) + + err := m.TDigest.Add(result.ResponseTime.Seconds()) + if err != nil { + } + + p95 := int(m.TDigest.Quantile(0.95) * 1000) + m.P95ResponseTimeWidget.Text = fmt.Sprintf("%d ms", p95) + sslExpiry := net.GetSSLCertExpiry(result.URL) m.SSLOkWidget.Text = fmt.Sprintf("%d days remaining", sslExpiry)