diff --git a/pkg/commands/container.go b/pkg/commands/container.go index fc9482fa..9c4b9d7a 100644 --- a/pkg/commands/container.go +++ b/pkg/commands/container.go @@ -39,6 +39,7 @@ type Container struct { type RecordedStats struct { ClientStats ContainerStats DerivedStats DerivedStats + RecordedAt time.Time } type DerivedStats struct { @@ -353,3 +354,18 @@ func (c *Container) Attach() (*exec.Cmd, error) { func (c *Container) Top() (types.ContainerProcessList, error) { return c.Client.ContainerTop(context.Background(), c.ID, []string{}) } + +// EraseOldHistory removes any history before the user-specified max duration +func (c *Container) EraseOldHistory() { + c.Log.Warn(c.Config.UserConfig.Stats.MaxDuration) + if c.Config.UserConfig.Stats.MaxDuration == 0 { + return + } + + for i, stat := range c.StatHistory { + if time.Since(stat.RecordedAt) < c.Config.UserConfig.Stats.MaxDuration { + c.StatHistory = c.StatHistory[i:] + return + } + } +} diff --git a/pkg/commands/container_stats.go b/pkg/commands/container_stats.go index 62aa7f51..e15e2856 100644 --- a/pkg/commands/container_stats.go +++ b/pkg/commands/container_stats.go @@ -197,15 +197,38 @@ func (c *Container) RenderStats(viewWidth int) (string, error) { // PlotGraph returns the plotted graph based on the graph spec and the stat history func (c *Container) PlotGraph(spec config.GraphConfig, width int) (string, error) { data := make([]float64, len(c.StatHistory)) + + max := spec.Max + min := spec.Min for i, stats := range c.StatHistory { value, err := lookup.LookupString(stats, spec.StatPath) if err != nil { - return "", err + return "Could not find key: " + spec.StatPath, nil } floatValue, err := getFloat(value.Interface()) if err != nil { return "", err } + if spec.MinType == "" { + if i == 0 { + min = floatValue + } else { + if floatValue < min { + min = floatValue + } + } + } + + if spec.MaxType == "" { + if i == 0 { + max = floatValue + } else { + if floatValue > max { + max = floatValue + } + } + } + data[i] = floatValue } @@ -213,8 +236,8 @@ func (c *Container) PlotGraph(spec config.GraphConfig, width int) (string, error data, asciigraph.Height(spec.Height), asciigraph.Width(width), - asciigraph.Min(spec.Min), - asciigraph.Max(spec.Max), + asciigraph.Min(min), + asciigraph.Max(max), asciigraph.Caption(spec.Caption), ), nil } diff --git a/pkg/commands/docker.go b/pkg/commands/docker.go index 43bdc703..c53e4585 100644 --- a/pkg/commands/docker.go +++ b/pkg/commands/docker.go @@ -130,11 +130,12 @@ func (c *DockerCommand) createClientStatMonitor(container *Container) { CPUPercentage: stats.CalculateContainerCPUPercentage(), MemoryPercentage: stats.CalculateContainerMemoryUsage(), }, + RecordedAt: time.Now(), } c.ContainerMutex.Lock() - // TODO: for now we never truncate the recorded stats, and we should container.StatHistory = append(container.StatHistory, recordedStats) + container.EraseOldHistory() c.ContainerMutex.Unlock() } diff --git a/pkg/config/app_config.go b/pkg/config/app_config.go index 8b7468c6..a059acd7 100644 --- a/pkg/config/app_config.go +++ b/pkg/config/app_config.go @@ -3,6 +3,7 @@ package config import ( "io/ioutil" "path/filepath" + "time" "github.com/shibukawa/configdir" yaml "gopkg.in/yaml.v2" @@ -123,10 +124,14 @@ type GraphConfig struct { Caption string `yaml:"caption,omitempty"` StatPath string `yaml:"statPath,omitempty"` Color string `yaml:"color,omitempty"` + // MinType and MaxType are each one of "", "static". blank means the min/max of the data set will be used. "static" means the min/max specified will be used + MinType string `yaml:"minType,omitempty"` + MaxType string `yaml:"maxType,omitempty"` } type StatsConfig struct { - Graphs []GraphConfig + Graphs []GraphConfig + MaxDuration time.Duration `yaml:"maxDuration,omitempty"` } // GetDefaultConfig returns the application default configuration diff --git a/pkg/gui/containers_panel.go b/pkg/gui/containers_panel.go index c92c48b0..6760f475 100644 --- a/pkg/gui/containers_panel.go +++ b/pkg/gui/containers_panel.go @@ -122,6 +122,7 @@ func (gui *Gui) renderContainerConfig(mainView *gocui.View, container *commands. func (gui *Gui) renderContainerStats(mainView *gocui.View, container *commands.Container) error { mainView.Autoscroll = false mainView.Title = "Stats" + mainView.Wrap = false return gui.T.NewTickerTask(time.Second, func() { width, _ := mainView.Size()