diff --git a/cmd/inbucket/main.go b/cmd/inbucket/main.go index b65c9ad..89fdb2a 100644 --- a/cmd/inbucket/main.go +++ b/cmd/inbucket/main.go @@ -40,10 +40,8 @@ var ( func init() { // Server uptime for status page. - startTime := time.Now() - expvar.Publish("uptime", expvar.Func(func() interface{} { - return time.Since(startTime) / time.Second - })) + startTime := expvar.NewInt("startMillis") + startTime.Set(time.Now().UnixNano() / 1000000) // Goroutine count for status page. expvar.Publish("goroutines", expvar.Func(func() interface{} { diff --git a/pkg/storage/retention.go b/pkg/storage/retention.go index 166da6b..ac33084 100644 --- a/pkg/storage/retention.go +++ b/pkg/storage/retention.go @@ -3,7 +3,6 @@ package storage import ( "container/list" "expvar" - "sync" "time" "github.com/jhillyerd/inbucket/pkg/config" @@ -12,8 +11,7 @@ import ( ) var ( - retentionScanCompleted = time.Now() - retentionScanCompletedMu sync.RWMutex + scanCompletedMillis = new(expvar.Int) // History counters expRetentionDeletesTotal = new(expvar.Int) @@ -34,7 +32,7 @@ var ( func init() { rm := expvar.NewMap("retention") - rm.Set("SecondsSinceScanCompleted", expvar.Func(secondsSinceRetentionScanCompleted)) + rm.Set("ScanCompletedMillis", scanCompletedMillis) rm.Set("DeletesHist", expRetentionDeletesHist) rm.Set("DeletesTotal", expRetentionDeletesTotal) rm.Set("Period", expRetentionPeriod) @@ -159,7 +157,7 @@ func (rs *RetentionScanner) DoScan() error { return err } // Update metrics - setRetentionScanCompleted(time.Now()) + scanCompletedMillis.Set(time.Now().UnixNano() / 1000000) expRetainedCurrent.Set(int64(retained)) expRetainedSize.Set(storeSize) return nil @@ -171,19 +169,3 @@ func (rs *RetentionScanner) Join() { <-rs.retentionShutdown } } - -func setRetentionScanCompleted(t time.Time) { - retentionScanCompletedMu.Lock() - defer retentionScanCompletedMu.Unlock() - retentionScanCompleted = t -} - -func getRetentionScanCompleted() time.Time { - retentionScanCompletedMu.RLock() - defer retentionScanCompletedMu.RUnlock() - return retentionScanCompleted -} - -func secondsSinceRetentionScanCompleted() interface{} { - return time.Since(getRetentionScanCompleted()) / time.Second -} diff --git a/ui/src/Data/Metrics.elm b/ui/src/Data/Metrics.elm index cbe0821..26b264e 100644 --- a/ui/src/Data/Metrics.elm +++ b/ui/src/Data/Metrics.elm @@ -1,11 +1,14 @@ module Data.Metrics exposing (Metrics, decodeIntList, decoder) +import Data.Date exposing (date) import Json.Decode as Decode exposing (..) import Json.Decode.Pipeline exposing (..) +import Time exposing (Posix) type alias Metrics = - { sysMem : Int + { startTime : Posix + , sysMem : Int , heapSize : Int , heapUsed : Int , heapObjects : Int @@ -26,12 +29,14 @@ type alias Metrics = , retainedCountHist : List Int , retainedSize : Int , retainedSizeHist : List Int + , scanCompleted : Posix } decoder : Decoder Metrics decoder = succeed Metrics + |> requiredAt [ "startMillis" ] date |> requiredAt [ "memstats", "Sys" ] int |> requiredAt [ "memstats", "HeapSys" ] int |> requiredAt [ "memstats", "HeapAlloc" ] int @@ -53,6 +58,7 @@ decoder = |> requiredAt [ "retention", "RetainedHist" ] decodeIntList |> requiredAt [ "retention", "RetainedSize" ] int |> requiredAt [ "retention", "SizeHist" ] decodeIntList + |> requiredAt [ "retention", "ScanCompletedMillis" ] date {-| Decodes Inbuckets hacky comma-separated-int JSON strings. diff --git a/ui/src/Page/Status.elm b/ui/src/Page/Status.elm index 4b6030d..e3aedd9 100644 --- a/ui/src/Page/Status.elm +++ b/ui/src/Page/Status.elm @@ -3,6 +3,7 @@ module Page.Status exposing (Model, Msg, init, subscriptions, update, view) import Data.Metrics as Metrics exposing (Metrics) import Data.ServerConfig as ServerConfig exposing (ServerConfig) import Data.Session as Session exposing (Session) +import DateFormat.Relative as Relative import Filesize import Html exposing (..) import Html.Attributes exposing (..) @@ -10,6 +11,7 @@ import Http exposing (Error) import HttpUtil import Sparkline as Spark import Svg.Attributes as SvgAttrib +import Task import Time exposing (Posix) @@ -18,7 +20,8 @@ import Time exposing (Posix) type alias Model = - { config : Maybe ServerConfig + { now : Posix + , config : Maybe ServerConfig , metrics : Maybe Metrics , xCounter : Float , sysMem : Metric @@ -50,7 +53,8 @@ type alias Metric = init : ( Model, Cmd Msg, Session.Msg ) init = - ( { config = Nothing + ( { now = Time.millisToPosix 0 + , config = Nothing , metrics = Nothing , xCounter = 60 , sysMem = Metric "System Memory" 0 Filesize.format graphZero initDataSet 10 @@ -62,13 +66,16 @@ init = , smtpConnOpen = Metric "Open Connections" 0 fmtInt graphZero initDataSet 10 , smtpConnTotal = Metric "Total Connections" 0 fmtInt graphChange initDataSet 60 , smtpReceivedTotal = Metric "Messages Received" 0 fmtInt graphChange initDataSet 60 - , smtpErrorsTotal = Metric "Messages Errors" 0 fmtInt graphChange initDataSet 60 - , smtpWarnsTotal = Metric "Messages Warns" 0 fmtInt graphChange initDataSet 60 + , smtpErrorsTotal = Metric "Message Errors" 0 fmtInt graphChange initDataSet 60 + , smtpWarnsTotal = Metric "Message Warnings" 0 fmtInt graphChange initDataSet 60 , retentionDeletesTotal = Metric "Retention Deletes" 0 fmtInt graphChange initDataSet 60 , retainedCount = Metric "Stored Messages" 0 fmtInt graphZero initDataSet 60 , retainedSize = Metric "Store Size" 0 Filesize.format graphZero initDataSet 60 } - , Cmd.batch [ loadServerConfig, loadMetrics ] + , Cmd.batch + [ Task.perform Tick Time.now + , loadServerConfig + ] , Session.none ) @@ -114,7 +121,7 @@ update session msg model = ( model, Cmd.none, Session.SetFlash (HttpUtil.errorString err) ) Tick time -> - ( model, loadMetrics, Session.none ) + ( { model | now = time }, loadMetrics, Session.none ) {-| Update all metrics in Model; increment xCounter. @@ -343,9 +350,12 @@ metricPanels model = Nothing -> [ text "Loading metrics..." ] - Just _ -> + Just metrics -> [ framePanel "General Metrics" - [ viewMetric model.sysMem + [ textEntry "Uptime" <| + "Started " + ++ Relative.relativeTime model.now metrics.startTime + , viewMetric model.sysMem , viewMetric model.heapSize , viewMetric model.heapUsed , viewMetric model.heapObjects @@ -360,13 +370,34 @@ metricPanels model = , viewMetric model.smtpWarnsTotal ] , framePanel "Storage Metrics" - [ viewMetric model.retentionDeletesTotal + [ textEntry "Retention Scan" (retentionScan model) + , viewMetric model.retentionDeletesTotal , viewMetric model.retainedCount , viewMetric model.retainedSize ] ] +retentionScan : Model -> String +retentionScan model = + case ( model.config, model.metrics ) of + ( Just config, Just metrics ) -> + case config.storageConfig.retentionPeriod of + "" -> + "Disabled" + + _ -> + case Time.posixToMillis metrics.scanCompleted of + 0 -> + "Not completed" + + _ -> + "Completed " ++ Relative.relativeTime model.now metrics.scanCompleted + + ( _, _ ) -> + "No data" + + textEntry : String -> String -> Html Msg textEntry name value = div [ class "metric" ]