From dbaebe019222a89f46d76a8661b1f7102dbe87ee Mon Sep 17 00:00:00 2001 From: f1yegor Date: Sun, 12 Mar 2017 02:35:40 +0100 Subject: [PATCH] add pids, IO stat --- .gitignore | 2 ++ cwidgets/compact/header.go | 4 +-- cwidgets/compact/main.go | 15 +++++++++-- cwidgets/compact/setters.go | 10 ++++++++ cwidgets/expanded/io.go | 51 +++++++++++++++++++++++++++++++++++++ cwidgets/expanded/main.go | 5 ++++ metrics/docker.go | 15 +++++++++++ metrics/main.go | 28 ++++++++++++-------- sort.go | 11 ++++++++ 9 files changed, 126 insertions(+), 15 deletions(-) create mode 100644 .gitignore create mode 100644 cwidgets/expanded/io.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6276bc9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +ctop +.idea \ No newline at end of file diff --git a/cwidgets/compact/header.go b/cwidgets/compact/header.go index 7dd8440..d4e3a39 100644 --- a/cwidgets/compact/header.go +++ b/cwidgets/compact/header.go @@ -12,7 +12,7 @@ type CompactHeader struct { } func NewCompactHeader() *CompactHeader { - fields := []string{"", "NAME", "CID", "CPU", "MEM", "NET RX/TX"} + fields := []string{"", "NAME", "CID", "CPU", "MEM", "NET RX/TX", "IO Read/Write", "Pids"} ch := &CompactHeader{} ch.Height = 2 for _, f := range fields { @@ -27,7 +27,7 @@ func (ch *CompactHeader) GetHeight() int { func (ch *CompactHeader) SetWidth(w int) { x := ch.X - autoWidth := calcWidth(w, 5) + autoWidth := calcWidth(w, 7) for n, col := range ch.pars { // set status column to static width if n == 0 { diff --git a/cwidgets/compact/main.go b/cwidgets/compact/main.go index b979040..b1764ea 100644 --- a/cwidgets/compact/main.go +++ b/cwidgets/compact/main.go @@ -15,6 +15,8 @@ type Compact struct { Cpu *GaugeCol Memory *GaugeCol Net *TextCol + IO *TextCol + Pids *TextCol X, Y int Width int Height int @@ -32,6 +34,8 @@ func NewCompact(id string) *Compact { Cpu: NewGaugeCol(), Memory: NewGaugeCol(), Net: NewTextCol("-"), + IO: NewTextCol("-"), + Pids: NewTextCol("-"), X: 1, Height: 1, } @@ -59,6 +63,8 @@ func (row *Compact) SetMetrics(m metrics.Metrics) { row.SetCPU(m.CPUUtil) row.SetNet(m.NetRx, m.NetTx) row.SetMem(m.MemUsage, m.MemLimit, m.MemPercent) + row.SetIO(m.IOBytesRead, m.IOBytesWrite) + row.SetPids(m.Pids) } // Set gauges, counters to default unread values @@ -66,6 +72,8 @@ func (row *Compact) Reset() { row.Cpu.Reset() row.Memory.Reset() row.Net.Reset() + row.IO.Reset() + row.Pids.Reset() } func (row *Compact) GetHeight() int { @@ -91,7 +99,7 @@ func (row *Compact) SetWidth(width int) { return } x := row.X - autoWidth := calcWidth(width, 5) + autoWidth := calcWidth(width, 7) for n, col := range row.all() { // set status column to static width if n == 0 { @@ -116,7 +124,8 @@ func (row *Compact) Buffer() ui.Buffer { buf.Merge(row.Cpu.Buffer()) buf.Merge(row.Memory.Buffer()) buf.Merge(row.Net.Buffer()) - + buf.Merge(row.IO.Buffer()) + buf.Merge(row.Pids.Buffer()) return buf } @@ -128,5 +137,7 @@ func (row *Compact) all() []ui.GridBufferer { row.Cpu, row.Memory, row.Net, + row.IO, + row.Pids, } } diff --git a/cwidgets/compact/setters.go b/cwidgets/compact/setters.go index 9db58e0..e23a383 100644 --- a/cwidgets/compact/setters.go +++ b/cwidgets/compact/setters.go @@ -13,6 +13,16 @@ func (row *Compact) SetNet(rx int64, tx int64) { row.Net.Set(label) } +func (row *Compact) SetIO(read int64, write int64) { + label := fmt.Sprintf("%s / %s", cwidgets.ByteFormat(read), cwidgets.ByteFormat(write)) + row.IO.Set(label) +} + +func (row *Compact) SetPids(val int) { + label := fmt.Sprintf("%s", strconv.Itoa(val)) + row.Pids.Set(label) +} + func (row *Compact) SetCPU(val int) { row.Cpu.BarColor = colorScale(val) row.Cpu.Label = fmt.Sprintf("%s%%", strconv.Itoa(val)) diff --git a/cwidgets/expanded/io.go b/cwidgets/expanded/io.go new file mode 100644 index 0000000..41fd93c --- /dev/null +++ b/cwidgets/expanded/io.go @@ -0,0 +1,51 @@ +package expanded + +import ( + "fmt" + "strings" + + "github.com/bcicen/ctop/cwidgets" + ui "github.com/gizak/termui" +) + +type IO struct { + *ui.Sparklines + readHist *DiffHist + writeHist *DiffHist +} + +func NewIO() *IO { + io := &IO{ui.NewSparklines(), NewDiffHist(60), NewDiffHist(60)} + io.BorderLabel = "IO" + io.Height = 6 + io.Width = colWidth[0] + io.X = 0 + io.Y = 24 + + read := ui.NewSparkline() + read.Title = "read" + read.Height = 1 + read.Data = io.readHist.Data + read.LineColor = ui.ColorGreen + + write := ui.NewSparkline() + write.Title = "write" + write.Height = 1 + write.Data = io.writeHist.Data + write.LineColor = ui.ColorYellow + + io.Lines = []ui.Sparkline{read, write} + return io +} + +func (w *IO) Update(read int64, write int64) { + var rate string + + w.readHist.Append(int(read)) + rate = strings.ToLower(cwidgets.ByteFormatInt(w.readHist.Val)) + w.Lines[0].Title = fmt.Sprintf("read [%s/s]", rate) + + w.writeHist.Append(int(write)) + rate = strings.ToLower(cwidgets.ByteFormatInt(w.writeHist.Val)) + w.Lines[1].Title = fmt.Sprintf("write [%s/s]", rate) +} diff --git a/cwidgets/expanded/main.go b/cwidgets/expanded/main.go index f180153..00d14a8 100644 --- a/cwidgets/expanded/main.go +++ b/cwidgets/expanded/main.go @@ -17,6 +17,7 @@ type Expanded struct { Net *Net Cpu *Cpu Mem *Mem + IO *IO Width int } @@ -29,6 +30,7 @@ func NewExpanded(id string) *Expanded { Net: NewNet(), Cpu: NewCpu(), Mem: NewMem(), + IO: NewIO(), Width: ui.TermWidth(), } } @@ -45,6 +47,7 @@ func (e *Expanded) SetMetrics(m metrics.Metrics) { e.Cpu.Update(m.CPUUtil) e.Net.Update(m.NetRx, m.NetTx) e.Mem.Update(int(m.MemUsage), int(m.MemLimit)) + e.IO.Update(m.IOBytesRead, m.IOBytesWrite) } func (e *Expanded) Align() { @@ -74,6 +77,7 @@ func (e *Expanded) Buffer() ui.Buffer { buf.Merge(e.Cpu.Buffer()) buf.Merge(e.Mem.Buffer()) buf.Merge(e.Net.Buffer()) + buf.Merge(e.IO.Buffer()) return buf } @@ -83,6 +87,7 @@ func (e *Expanded) all() []ui.GridBufferer { e.Cpu, e.Mem, e.Net, + e.IO, } } diff --git a/metrics/docker.go b/metrics/docker.go index c08aff1..070ddcd 100644 --- a/metrics/docker.go +++ b/metrics/docker.go @@ -46,6 +46,7 @@ func (c *Docker) Start() { c.ReadCPU(s) c.ReadMem(s) c.ReadNet(s) + c.ReadIO(s) c.stream <- c.Metrics } log.Infof("collector stopped for container: %s", c.id) @@ -79,6 +80,7 @@ func (c *Docker) ReadCPU(stats *api.Stats) { c.CPUUtil = round((cpudiff / syscpudiff * 100) * ncpus) c.lastCpu = total c.lastSysCpu = system + c.Pids = int(stats.PidsStats.Current) } func (c *Docker) ReadMem(stats *api.Stats) { @@ -95,3 +97,16 @@ func (c *Docker) ReadNet(stats *api.Stats) { } c.NetRx, c.NetTx = rx, tx } + +func (c *Docker) ReadIO(stats *api.Stats) { + var read, write int64 + for _, blk := range stats.BlkioStats.IOServiceBytesRecursive { + if blk.Op == "Read" { + read = int64(blk.Value) + } + if blk.Op == "Write" { + write = int64(blk.Value) + } + } + c.IOBytesRead, c.IOBytesWrite = read, write +} diff --git a/metrics/main.go b/metrics/main.go index 0aa8c55..387b01d 100644 --- a/metrics/main.go +++ b/metrics/main.go @@ -9,21 +9,27 @@ import ( var log = logging.Init() type Metrics struct { - CPUUtil int - NetTx int64 - NetRx int64 - MemLimit int64 - MemPercent int - MemUsage int64 + CPUUtil int + NetTx int64 + NetRx int64 + MemLimit int64 + MemPercent int + MemUsage int64 + IOBytesRead int64 + IOBytesWrite int64 + Pids int } func NewMetrics() Metrics { return Metrics{ - CPUUtil: -1, - NetTx: -1, - NetRx: -1, - MemUsage: -1, - MemPercent: -1, + CPUUtil: -1, + NetTx: -1, + NetRx: -1, + MemUsage: -1, + MemPercent: -1, + IOBytesRead: -1, + IOBytesWrite: -1, + Pids: -1, } } diff --git a/sort.go b/sort.go index 1af108c..d7d77c3 100644 --- a/sort.go +++ b/sort.go @@ -53,6 +53,15 @@ var Sorters = map[string]sortMethod{ } return sum1 > sum2 }, + "io": func(c1, c2 *Container) bool { + sum1 := sumIO(c1) + sum2 := sumIO(c2) + // Use secondary sort method if equal values + if sum1 == sum2 { + return nameSorter(c1, c2) + } + return sum1 > sum2 + }, "state": func(c1, c2 *Container) bool { // Use secondary sort method if equal values c1state := c1.GetMeta("state") @@ -101,3 +110,5 @@ func (a Containers) Filter() { } func sumNet(c *Container) int64 { return c.NetRx + c.NetTx } + +func sumIO(c *Container) int64 { return c.IOBytesRead + c.IOBytesWrite }