mirror of
https://github.com/bcicen/ctop.git
synced 2025-12-06 23:26:45 +08:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a2011b8bc7 | ||
|
|
40fd9e935a | ||
|
|
b88c143914 | ||
|
|
0a05007c4e | ||
|
|
c47ba3f804 | ||
|
|
79a3f361a7 | ||
|
|
65399a37e5 | ||
|
|
25a3fcf731 | ||
|
|
17e2c2df8e | ||
|
|
240345d527 | ||
|
|
2d284d9277 | ||
|
|
bfa5c5944f | ||
|
|
e1051cd40f | ||
|
|
13029cc7fe | ||
|
|
58d9e4e194 | ||
|
|
4de7036e2f |
15
Dockerfile
15
Dockerfile
@@ -1,3 +1,16 @@
|
|||||||
|
FROM quay.io/vektorcloud/go:1.8
|
||||||
|
|
||||||
|
RUN apk add --no-cache make
|
||||||
|
|
||||||
|
COPY glide.* /go/src/github.com/bcicen/ctop/
|
||||||
|
WORKDIR /go/src/github.com/bcicen/ctop/
|
||||||
|
RUN glide install
|
||||||
|
|
||||||
|
COPY . /go/src/github.com/bcicen/ctop
|
||||||
|
RUN make build && \
|
||||||
|
mkdir -p /go/bin && \
|
||||||
|
mv -v ctop /go/bin/
|
||||||
|
|
||||||
FROM scratch
|
FROM scratch
|
||||||
COPY ./ctop /ctop
|
COPY --from=0 /go/bin/ctop /ctop
|
||||||
ENTRYPOINT ["/ctop"]
|
ENTRYPOINT ["/ctop"]
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
FROM quay.io/vektorcloud/go:1.8
|
|
||||||
|
|
||||||
RUN apk add --no-cache make
|
|
||||||
|
|
||||||
COPY glide.* /go/src/github.com/bcicen/ctop/
|
|
||||||
WORKDIR /go/src/github.com/bcicen/ctop/
|
|
||||||
RUN glide install
|
|
||||||
|
|
||||||
COPY . /go/src/github.com/bcicen/ctop
|
|
||||||
RUN make build && \
|
|
||||||
mkdir -p /go/bin && \
|
|
||||||
mv -v ctop /go/bin/
|
|
||||||
17
Makefile
17
Makefile
@@ -5,7 +5,7 @@ EXT_LD_FLAGS="-Wl,--allow-multiple-definition"
|
|||||||
LD_FLAGS="-w -X main.version=$(VERSION) -X main.build=$(BUILD) -extldflags=$(EXT_LD_FLAGS)"
|
LD_FLAGS="-w -X main.version=$(VERSION) -X main.build=$(BUILD) -extldflags=$(EXT_LD_FLAGS)"
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
rm -rf build/ release/
|
rm -rf _build/ _release/
|
||||||
|
|
||||||
build:
|
build:
|
||||||
glide install
|
glide install
|
||||||
@@ -16,21 +16,18 @@ build-dev:
|
|||||||
|
|
||||||
build-all:
|
build-all:
|
||||||
mkdir -p build
|
mkdir -p build
|
||||||
GOOS=darwin GOARCH=amd64 go build -tags release -ldflags $(LD_FLAGS) -o build/ctop-$(VERSION)-darwin-amd64
|
GOOS=darwin GOARCH=amd64 go build -tags release -ldflags $(LD_FLAGS) -o _build/ctop-$(VERSION)-darwin-amd64
|
||||||
GOOS=linux GOARCH=amd64 go build -tags release -ldflags $(LD_FLAGS) -o build/ctop-$(VERSION)-linux-amd64
|
GOOS=linux GOARCH=amd64 go build -tags release -ldflags $(LD_FLAGS) -o _build/ctop-$(VERSION)-linux-amd64
|
||||||
GOOS=linux GOARCH=arm go build -tags release -ldflags $(LD_FLAGS) -o build/ctop-$(VERSION)-linux-arm
|
GOOS=linux GOARCH=arm go build -tags release -ldflags $(LD_FLAGS) -o _build/ctop-$(VERSION)-linux-arm
|
||||||
GOOS=linux GOARCH=arm64 go build -tags release -ldflags $(LD_FLAGS) -o build/ctop-$(VERSION)-linux-arm64
|
GOOS=linux GOARCH=arm64 go build -tags release -ldflags $(LD_FLAGS) -o _build/ctop-$(VERSION)-linux-arm64
|
||||||
|
|
||||||
image:
|
image:
|
||||||
docker build -t ctop_build -f Dockerfile_build .
|
|
||||||
docker create --name=ctop_built ctop_build ctop -v
|
|
||||||
docker cp ctop_built:/go/bin/ctop .
|
|
||||||
docker build -t ctop -f Dockerfile .
|
docker build -t ctop -f Dockerfile .
|
||||||
|
|
||||||
release:
|
release:
|
||||||
mkdir release
|
mkdir _release
|
||||||
go get github.com/progrium/gh-release/...
|
go get github.com/progrium/gh-release/...
|
||||||
cp build/* release
|
cp _build/* _release
|
||||||
gh-release create bcicen/$(NAME) $(VERSION) \
|
gh-release create bcicen/$(NAME) $(VERSION) \
|
||||||
$(shell git rev-parse --abbrev-ref HEAD) $(VERSION)
|
$(shell git rev-parse --abbrev-ref HEAD) $(VERSION)
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ Fetch the [latest release](https://github.com/bcicen/ctop/releases) for your pla
|
|||||||
#### Linux
|
#### Linux
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo wget https://github.com/bcicen/ctop/releases/download/v0.6/ctop-0.6-linux-amd64 -O /usr/local/bin/ctop
|
sudo wget https://github.com/bcicen/ctop/releases/download/v0.6.0/ctop-0.6.0-linux-amd64 -O /usr/local/bin/ctop
|
||||||
sudo chmod +x /usr/local/bin/ctop
|
sudo chmod +x /usr/local/bin/ctop
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -31,7 +31,7 @@ brew install ctop
|
|||||||
```
|
```
|
||||||
or
|
or
|
||||||
```bash
|
```bash
|
||||||
sudo curl -Lo /usr/local/bin/ctop https://github.com/bcicen/ctop/releases/download/v0.6/ctop-0.6-darwin-amd64
|
sudo curl -Lo /usr/local/bin/ctop https://github.com/bcicen/ctop/releases/download/v0.6.0/ctop-0.6.0-darwin-amd64
|
||||||
sudo chmod +x /usr/local/bin/ctop
|
sudo chmod +x /usr/local/bin/ctop
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
package collector
|
package collector
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/bcicen/ctop/metrics"
|
"github.com/bcicen/ctop/models"
|
||||||
api "github.com/fsouza/go-dockerclient"
|
api "github.com/fsouza/go-dockerclient"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Docker collector
|
// Docker collector
|
||||||
type Docker struct {
|
type Docker struct {
|
||||||
metrics.Metrics
|
models.Metrics
|
||||||
id string
|
id string
|
||||||
client *api.Client
|
client *api.Client
|
||||||
running bool
|
running bool
|
||||||
stream chan metrics.Metrics
|
stream chan models.Metrics
|
||||||
done chan bool
|
done chan bool
|
||||||
lastCpu float64
|
lastCpu float64
|
||||||
lastSysCpu float64
|
lastSysCpu float64
|
||||||
@@ -19,7 +19,7 @@ type Docker struct {
|
|||||||
|
|
||||||
func NewDocker(client *api.Client, id string) *Docker {
|
func NewDocker(client *api.Client, id string) *Docker {
|
||||||
return &Docker{
|
return &Docker{
|
||||||
Metrics: metrics.Metrics{},
|
Metrics: models.Metrics{},
|
||||||
id: id,
|
id: id,
|
||||||
client: client,
|
client: client,
|
||||||
}
|
}
|
||||||
@@ -27,7 +27,7 @@ func NewDocker(client *api.Client, id string) *Docker {
|
|||||||
|
|
||||||
func (c *Docker) Start() {
|
func (c *Docker) Start() {
|
||||||
c.done = make(chan bool)
|
c.done = make(chan bool)
|
||||||
c.stream = make(chan metrics.Metrics)
|
c.stream = make(chan models.Metrics)
|
||||||
stats := make(chan *api.Stats)
|
stats := make(chan *api.Stats)
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
@@ -61,10 +61,14 @@ func (c *Docker) Running() bool {
|
|||||||
return c.running
|
return c.running
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Docker) Stream() chan metrics.Metrics {
|
func (c *Docker) Stream() chan models.Metrics {
|
||||||
return c.stream
|
return c.stream
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Docker) Logs() LogCollector {
|
||||||
|
return &DockerLogs{c.id, c.client, make(chan bool)}
|
||||||
|
}
|
||||||
|
|
||||||
// Stop collector
|
// Stop collector
|
||||||
func (c *Docker) Stop() {
|
func (c *Docker) Stop() {
|
||||||
c.done <- true
|
c.done <- true
|
||||||
|
|||||||
74
connector/collector/docker_logs.go
Normal file
74
connector/collector/docker_logs.go
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
package collector
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/bcicen/ctop/models"
|
||||||
|
api "github.com/fsouza/go-dockerclient"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DockerLogs struct {
|
||||||
|
id string
|
||||||
|
client *api.Client
|
||||||
|
done chan bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *DockerLogs) Stream() chan models.Log {
|
||||||
|
r, w := io.Pipe()
|
||||||
|
logCh := make(chan models.Log)
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
opts := api.LogsOptions{
|
||||||
|
Context: ctx,
|
||||||
|
Container: l.id,
|
||||||
|
OutputStream: w,
|
||||||
|
ErrorStream: w,
|
||||||
|
Stdout: true,
|
||||||
|
Stderr: true,
|
||||||
|
Tail: "10",
|
||||||
|
Follow: true,
|
||||||
|
Timestamps: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// read io pipe into channel
|
||||||
|
go func() {
|
||||||
|
scanner := bufio.NewScanner(r)
|
||||||
|
for scanner.Scan() {
|
||||||
|
parts := strings.Split(scanner.Text(), " ")
|
||||||
|
ts := l.parseTime(parts[0])
|
||||||
|
logCh <- models.Log{ts, strings.Join(parts[1:], " ")}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// connect to container log stream
|
||||||
|
go func() {
|
||||||
|
err := l.client.Logs(opts)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("error reading container logs: %s", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
select {
|
||||||
|
case <-l.done:
|
||||||
|
cancel()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return logCh
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *DockerLogs) Stop() { l.done <- true }
|
||||||
|
|
||||||
|
func (l *DockerLogs) parseTime(s string) time.Time {
|
||||||
|
ts, err := time.Parse("2006-01-02T15:04:05.000000000Z", s)
|
||||||
|
if err != nil {
|
||||||
|
log.Errorf("failed to parse container log: %s", err)
|
||||||
|
ts = time.Now()
|
||||||
|
}
|
||||||
|
return ts
|
||||||
|
}
|
||||||
@@ -4,10 +4,24 @@ import (
|
|||||||
"math"
|
"math"
|
||||||
|
|
||||||
"github.com/bcicen/ctop/logging"
|
"github.com/bcicen/ctop/logging"
|
||||||
|
"github.com/bcicen/ctop/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
var log = logging.Init()
|
var log = logging.Init()
|
||||||
|
|
||||||
|
type LogCollector interface {
|
||||||
|
Stream() chan models.Log
|
||||||
|
Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
type Collector interface {
|
||||||
|
Stream() chan models.Metrics
|
||||||
|
Logs() LogCollector
|
||||||
|
Running() bool
|
||||||
|
Start()
|
||||||
|
Stop()
|
||||||
|
}
|
||||||
|
|
||||||
func round(num float64) int {
|
func round(num float64) int {
|
||||||
return int(num + math.Copysign(0.5, num))
|
return int(num + math.Copysign(0.5, num))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ import (
|
|||||||
"math/rand"
|
"math/rand"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bcicen/ctop/metrics"
|
"github.com/bcicen/ctop/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Mock collector
|
// Mock collector
|
||||||
type Mock struct {
|
type Mock struct {
|
||||||
metrics.Metrics
|
models.Metrics
|
||||||
stream chan metrics.Metrics
|
stream chan models.Metrics
|
||||||
done bool
|
done bool
|
||||||
running bool
|
running bool
|
||||||
aggression int64
|
aggression int64
|
||||||
@@ -20,7 +20,7 @@ type Mock struct {
|
|||||||
|
|
||||||
func NewMock(a int64) *Mock {
|
func NewMock(a int64) *Mock {
|
||||||
c := &Mock{
|
c := &Mock{
|
||||||
Metrics: metrics.Metrics{},
|
Metrics: models.Metrics{},
|
||||||
aggression: a,
|
aggression: a,
|
||||||
}
|
}
|
||||||
c.MemLimit = 2147483648
|
c.MemLimit = 2147483648
|
||||||
@@ -33,7 +33,7 @@ func (c *Mock) Running() bool {
|
|||||||
|
|
||||||
func (c *Mock) Start() {
|
func (c *Mock) Start() {
|
||||||
c.done = false
|
c.done = false
|
||||||
c.stream = make(chan metrics.Metrics)
|
c.stream = make(chan models.Metrics)
|
||||||
go c.run()
|
go c.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -41,10 +41,14 @@ func (c *Mock) Stop() {
|
|||||||
c.done = true
|
c.done = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Mock) Stream() chan metrics.Metrics {
|
func (c *Mock) Stream() chan models.Metrics {
|
||||||
return c.stream
|
return c.stream
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Mock) Logs() LogCollector {
|
||||||
|
return &MockLogs{make(chan bool)}
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Mock) run() {
|
func (c *Mock) run() {
|
||||||
c.running = true
|
c.running = true
|
||||||
rand.Seed(int64(time.Now().Nanosecond()))
|
rand.Seed(int64(time.Now().Nanosecond()))
|
||||||
|
|||||||
31
connector/collector/mock_logs.go
Normal file
31
connector/collector/mock_logs.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package collector
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/bcicen/ctop/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
const mockLog = "Cura ob pro qui tibi inveni dum qua fit donec amare illic mea, regem falli contexo pro peregrinorum heremo absconditi araneae meminerim deliciosas actionibus facere modico dura sonuerunt psalmi contra rerum, tempus mala anima volebant dura quae o modis."
|
||||||
|
|
||||||
|
type MockLogs struct {
|
||||||
|
done chan bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *MockLogs) Stream() chan models.Log {
|
||||||
|
logCh := make(chan models.Log)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case <-l.done:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
logCh <- models.Log{time.Now(), mockLog}
|
||||||
|
time.Sleep(250 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return logCh
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *MockLogs) Stop() { l.done <- true }
|
||||||
@@ -5,17 +5,17 @@ package collector
|
|||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bcicen/ctop/metrics"
|
"github.com/bcicen/ctop/models"
|
||||||
"github.com/opencontainers/runc/libcontainer"
|
"github.com/opencontainers/runc/libcontainer"
|
||||||
"github.com/opencontainers/runc/libcontainer/cgroups"
|
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Runc collector
|
// Runc collector
|
||||||
type Runc struct {
|
type Runc struct {
|
||||||
metrics.Metrics
|
models.Metrics
|
||||||
id string
|
id string
|
||||||
libc libcontainer.Container
|
libc libcontainer.Container
|
||||||
stream chan metrics.Metrics
|
stream chan models.Metrics
|
||||||
done bool
|
done bool
|
||||||
running bool
|
running bool
|
||||||
interval int // collection interval, in seconds
|
interval int // collection interval, in seconds
|
||||||
@@ -25,7 +25,7 @@ type Runc struct {
|
|||||||
|
|
||||||
func NewRunc(libc libcontainer.Container) *Runc {
|
func NewRunc(libc libcontainer.Container) *Runc {
|
||||||
c := &Runc{
|
c := &Runc{
|
||||||
Metrics: metrics.Metrics{},
|
Metrics: models.Metrics{},
|
||||||
id: libc.ID(),
|
id: libc.ID(),
|
||||||
libc: libc,
|
libc: libc,
|
||||||
interval: 1,
|
interval: 1,
|
||||||
@@ -39,7 +39,7 @@ func (c *Runc) Running() bool {
|
|||||||
|
|
||||||
func (c *Runc) Start() {
|
func (c *Runc) Start() {
|
||||||
c.done = false
|
c.done = false
|
||||||
c.stream = make(chan metrics.Metrics)
|
c.stream = make(chan models.Metrics)
|
||||||
go c.run()
|
go c.run()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,10 +47,14 @@ func (c *Runc) Stop() {
|
|||||||
c.done = true
|
c.done = true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Runc) Stream() chan metrics.Metrics {
|
func (c *Runc) Stream() chan models.Metrics {
|
||||||
return c.stream
|
return c.stream
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (c *Runc) Logs() LogCollector {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c *Runc) run() {
|
func (c *Runc) run() {
|
||||||
c.running = true
|
c.running = true
|
||||||
defer close(c.stream)
|
defer close(c.stream)
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ func portsFormat(ports map[api.Port][]api.PortBinding) string {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for _, binding := range v {
|
for _, binding := range v {
|
||||||
s := fmt.Sprintf("%s -> %s:%s", k, binding.HostIP, binding.HostPort)
|
s := fmt.Sprintf("%s:%s -> %s", binding.HostIP, binding.HostPort, k)
|
||||||
published = append(published, s)
|
published = append(published, s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
package container
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/bcicen/ctop/connector/collector"
|
||||||
"github.com/bcicen/ctop/cwidgets"
|
"github.com/bcicen/ctop/cwidgets"
|
||||||
"github.com/bcicen/ctop/cwidgets/compact"
|
"github.com/bcicen/ctop/cwidgets/compact"
|
||||||
"github.com/bcicen/ctop/logging"
|
"github.com/bcicen/ctop/logging"
|
||||||
"github.com/bcicen/ctop/metrics"
|
"github.com/bcicen/ctop/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -13,19 +14,19 @@ var (
|
|||||||
|
|
||||||
// Metrics and metadata representing a container
|
// Metrics and metadata representing a container
|
||||||
type Container struct {
|
type Container struct {
|
||||||
metrics.Metrics
|
models.Metrics
|
||||||
Id string
|
Id string
|
||||||
Meta map[string]string
|
Meta map[string]string
|
||||||
Widgets *compact.Compact
|
Widgets *compact.Compact
|
||||||
Display bool // display this container in compact view
|
Display bool // display this container in compact view
|
||||||
updater cwidgets.WidgetUpdater
|
updater cwidgets.WidgetUpdater
|
||||||
collector metrics.Collector
|
collector collector.Collector
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(id string, collector metrics.Collector) *Container {
|
func New(id string, collector collector.Collector) *Container {
|
||||||
widgets := compact.NewCompact(id)
|
widgets := compact.NewCompact(id)
|
||||||
return &Container{
|
return &Container{
|
||||||
Metrics: metrics.NewMetrics(),
|
Metrics: models.NewMetrics(),
|
||||||
Id: id,
|
Id: id,
|
||||||
Meta: make(map[string]string),
|
Meta: make(map[string]string),
|
||||||
Widgets: widgets,
|
Widgets: widgets,
|
||||||
@@ -66,15 +67,20 @@ func (c *Container) SetState(s string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return container log collector
|
||||||
|
func (c *Container) Logs() collector.LogCollector {
|
||||||
|
return c.collector.Logs()
|
||||||
|
}
|
||||||
|
|
||||||
// Read metric stream, updating widgets
|
// Read metric stream, updating widgets
|
||||||
func (c *Container) Read(stream chan metrics.Metrics) {
|
func (c *Container) Read(stream chan models.Metrics) {
|
||||||
go func() {
|
go func() {
|
||||||
for metrics := range stream {
|
for metrics := range stream {
|
||||||
c.Metrics = metrics
|
c.Metrics = metrics
|
||||||
c.updater.SetMetrics(metrics)
|
c.updater.SetMetrics(metrics)
|
||||||
}
|
}
|
||||||
log.Infof("reader stopped for container: %s", c.Id)
|
log.Infof("reader stopped for container: %s", c.Id)
|
||||||
c.Metrics = metrics.NewMetrics()
|
c.Metrics = models.NewMetrics()
|
||||||
c.Widgets.Reset()
|
c.Widgets.Reset()
|
||||||
}()
|
}()
|
||||||
log.Infof("reader started for container: %s", c.Id)
|
log.Infof("reader started for container: %s", c.Id)
|
||||||
28
cursor.go
28
cursor.go
@@ -142,10 +142,11 @@ func (gc *GridCursor) PgUp() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var nextidx int
|
nextidx := int(math.Max(0.0, float64(idx-cGrid.MaxRows())))
|
||||||
nextidx = int(math.Max(0.0, float64(idx-cGrid.MaxRows())))
|
if gc.pgCount() > 0 {
|
||||||
cGrid.Offset = int(math.Max(float64(cGrid.Offset-cGrid.MaxRows()),
|
cGrid.Offset = int(math.Max(float64(cGrid.Offset-cGrid.MaxRows()),
|
||||||
float64(0)))
|
float64(0)))
|
||||||
|
}
|
||||||
|
|
||||||
active := gc.filtered[idx]
|
active := gc.filtered[idx]
|
||||||
next := gc.filtered[nextidx]
|
next := gc.filtered[nextidx]
|
||||||
@@ -164,11 +165,11 @@ func (gc *GridCursor) PgDown() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var nextidx int
|
nextidx := int(math.Min(float64(gc.Len()-1), float64(idx+cGrid.MaxRows())))
|
||||||
nextidx = int(math.Min(float64(gc.Len()-1),
|
if gc.pgCount() > 0 {
|
||||||
float64(idx+cGrid.MaxRows())))
|
cGrid.Offset = int(math.Min(float64(cGrid.Offset+cGrid.MaxRows()),
|
||||||
cGrid.Offset = int(math.Min(float64(cGrid.Offset+cGrid.MaxRows()),
|
float64(gc.Len()-cGrid.MaxRows())))
|
||||||
float64(gc.Len()-cGrid.MaxRows())))
|
}
|
||||||
|
|
||||||
active := gc.filtered[idx]
|
active := gc.filtered[idx]
|
||||||
next := gc.filtered[nextidx]
|
next := gc.filtered[nextidx]
|
||||||
@@ -180,3 +181,12 @@ func (gc *GridCursor) PgDown() {
|
|||||||
cGrid.Align()
|
cGrid.Align()
|
||||||
ui.Render(cGrid)
|
ui.Render(cGrid)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// number of pages at current row count and term height
|
||||||
|
func (gc *GridCursor) pgCount() int {
|
||||||
|
pages := gc.Len() / cGrid.MaxRows()
|
||||||
|
if gc.Len()%cGrid.MaxRows() > 0 {
|
||||||
|
pages++
|
||||||
|
}
|
||||||
|
return pages
|
||||||
|
}
|
||||||
|
|||||||
@@ -22,9 +22,14 @@ func NewCompactGrid() *CompactGrid {
|
|||||||
|
|
||||||
func (cg *CompactGrid) Align() {
|
func (cg *CompactGrid) Align() {
|
||||||
y := cg.Y
|
y := cg.Y
|
||||||
|
|
||||||
if cg.Offset >= len(cg.Rows) {
|
if cg.Offset >= len(cg.Rows) {
|
||||||
cg.Offset = 0
|
cg.Offset = 0
|
||||||
}
|
}
|
||||||
|
if cg.Offset < 0 {
|
||||||
|
cg.Offset = 0
|
||||||
|
}
|
||||||
|
|
||||||
// update row ypos, width recursively
|
// update row ypos, width recursively
|
||||||
for _, r := range cg.pageRows() {
|
for _, r := range cg.pageRows() {
|
||||||
r.SetY(y)
|
r.SetY(y)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ package compact
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/bcicen/ctop/logging"
|
"github.com/bcicen/ctop/logging"
|
||||||
"github.com/bcicen/ctop/metrics"
|
"github.com/bcicen/ctop/models"
|
||||||
ui "github.com/gizak/termui"
|
ui "github.com/gizak/termui"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -59,7 +59,7 @@ func (row *Compact) SetMeta(k, v string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (row *Compact) SetMetrics(m metrics.Metrics) {
|
func (row *Compact) SetMetrics(m models.Metrics) {
|
||||||
row.SetCPU(m.CPUUtil)
|
row.SetCPU(m.CPUUtil)
|
||||||
row.SetNet(m.NetRx, m.NetTx)
|
row.SetNet(m.NetRx, m.NetTx)
|
||||||
row.SetMem(m.MemUsage, m.MemLimit, m.MemPercent)
|
row.SetMem(m.MemUsage, m.MemLimit, m.MemPercent)
|
||||||
|
|||||||
83
cwidgets/expanded/logs.go
Normal file
83
cwidgets/expanded/logs.go
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
package expanded
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/bcicen/ctop/models"
|
||||||
|
ui "github.com/gizak/termui"
|
||||||
|
)
|
||||||
|
|
||||||
|
type LogLines struct {
|
||||||
|
ts []time.Time
|
||||||
|
data []string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLogLines(max int) *LogLines {
|
||||||
|
ll := &LogLines{
|
||||||
|
ts: make([]time.Time, max),
|
||||||
|
data: make([]string, max),
|
||||||
|
}
|
||||||
|
return ll
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ll *LogLines) tail(n int) []string {
|
||||||
|
lines := make([]string, n)
|
||||||
|
for i := 0; i < n; i++ {
|
||||||
|
lines = append(lines, ll.data[len(ll.data)-i])
|
||||||
|
}
|
||||||
|
return lines
|
||||||
|
}
|
||||||
|
func (ll *LogLines) getLines(start, end int) []string {
|
||||||
|
if end < 0 {
|
||||||
|
return ll.data[start:]
|
||||||
|
}
|
||||||
|
return ll.data[start:end]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ll *LogLines) add(l models.Log) {
|
||||||
|
if len(ll.data) == cap(ll.data) {
|
||||||
|
ll.data = append(ll.data[:0], ll.data[1:]...)
|
||||||
|
ll.ts = append(ll.ts[:0], ll.ts[1:]...)
|
||||||
|
}
|
||||||
|
ll.ts = append(ll.ts, l.Timestamp)
|
||||||
|
ll.data = append(ll.data, l.Message)
|
||||||
|
log.Debugf("recorded log line: %v", l)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Logs struct {
|
||||||
|
*ui.List
|
||||||
|
lines *LogLines
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewLogs(stream chan models.Log) *Logs {
|
||||||
|
p := ui.NewList()
|
||||||
|
p.Y = ui.TermHeight() / 2
|
||||||
|
p.X = 0
|
||||||
|
p.Height = ui.TermHeight() - p.Y
|
||||||
|
p.Width = ui.TermWidth()
|
||||||
|
//p.Overflow = "wrap"
|
||||||
|
p.ItemFgColor = ui.ThemeAttr("par.text.fg")
|
||||||
|
i := &Logs{p, NewLogLines(4098)}
|
||||||
|
go func() {
|
||||||
|
for line := range stream {
|
||||||
|
i.lines.add(line)
|
||||||
|
ui.Render(i)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
return i
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Logs) Align() {
|
||||||
|
w.X = colWidth[0]
|
||||||
|
w.List.Align()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *Logs) Buffer() ui.Buffer {
|
||||||
|
maxLines := w.Height - 2
|
||||||
|
offset := len(w.lines.data) - maxLines
|
||||||
|
w.Items = w.lines.getLines(offset, -1)
|
||||||
|
return w.List.Buffer()
|
||||||
|
}
|
||||||
|
|
||||||
|
// number of rows a line will occupy at current panel width
|
||||||
|
func (w *Logs) lineHeight(s string) int { return (len(s) / w.InnerWidth()) + 1 }
|
||||||
@@ -2,7 +2,7 @@ package expanded
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/bcicen/ctop/logging"
|
"github.com/bcicen/ctop/logging"
|
||||||
"github.com/bcicen/ctop/metrics"
|
"github.com/bcicen/ctop/models"
|
||||||
ui "github.com/gizak/termui"
|
ui "github.com/gizak/termui"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -55,7 +55,7 @@ func (e *Expanded) Down() {
|
|||||||
func (e *Expanded) SetWidth(w int) { e.Width = w }
|
func (e *Expanded) SetWidth(w int) { e.Width = w }
|
||||||
func (e *Expanded) SetMeta(k, v string) { e.Info.Set(k, v) }
|
func (e *Expanded) SetMeta(k, v string) { e.Info.Set(k, v) }
|
||||||
|
|
||||||
func (e *Expanded) SetMetrics(m metrics.Metrics) {
|
func (e *Expanded) SetMetrics(m models.Metrics) {
|
||||||
e.Cpu.Update(m.CPUUtil)
|
e.Cpu.Update(m.CPUUtil)
|
||||||
e.Net.Update(m.NetRx, m.NetTx)
|
e.Net.Update(m.NetRx, m.NetTx)
|
||||||
e.Mem.Update(int(m.MemUsage), int(m.MemLimit))
|
e.Mem.Update(int(m.MemUsage), int(m.MemLimit))
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ package cwidgets
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/bcicen/ctop/logging"
|
"github.com/bcicen/ctop/logging"
|
||||||
"github.com/bcicen/ctop/metrics"
|
"github.com/bcicen/ctop/models"
|
||||||
)
|
)
|
||||||
|
|
||||||
var log = logging.Init()
|
var log = logging.Init()
|
||||||
|
|
||||||
type WidgetUpdater interface {
|
type WidgetUpdater interface {
|
||||||
SetMeta(string, string)
|
SetMeta(string, string)
|
||||||
SetMetrics(metrics.Metrics)
|
SetMetrics(models.Metrics)
|
||||||
}
|
}
|
||||||
|
|||||||
19
debug.go
19
debug.go
@@ -3,11 +3,14 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
"github.com/bcicen/ctop/container"
|
"github.com/bcicen/ctop/container"
|
||||||
ui "github.com/gizak/termui"
|
ui "github.com/gizak/termui"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var mstats = &runtime.MemStats{}
|
||||||
|
|
||||||
func logEvent(e ui.Event) {
|
func logEvent(e ui.Event) {
|
||||||
var s string
|
var s string
|
||||||
s += fmt.Sprintf("Type=%s", quote(e.Type))
|
s += fmt.Sprintf("Type=%s", quote(e.Type))
|
||||||
@@ -19,6 +22,22 @@ func logEvent(e ui.Event) {
|
|||||||
log.Debugf("new event: %s", s)
|
log.Debugf("new event: %s", s)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func runtimeStats() {
|
||||||
|
var msg string
|
||||||
|
msg += fmt.Sprintf("cgo calls=%v", runtime.NumCgoCall())
|
||||||
|
msg += fmt.Sprintf(" routines=%v", runtime.NumGoroutine())
|
||||||
|
runtime.ReadMemStats(mstats)
|
||||||
|
msg += fmt.Sprintf(" numgc=%v", mstats.NumGC)
|
||||||
|
msg += fmt.Sprintf(" alloc=%v", mstats.Alloc)
|
||||||
|
log.Debugf("runtime: %v", msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runtimeStack() {
|
||||||
|
buf := make([]byte, 32768)
|
||||||
|
buf = buf[:runtime.Stack(buf, true)]
|
||||||
|
log.Infof(fmt.Sprintf("stack:\n%v", string(buf)))
|
||||||
|
}
|
||||||
|
|
||||||
// log container, metrics, and widget state
|
// log container, metrics, and widget state
|
||||||
func dumpContainer(c *container.Container) {
|
func dumpContainer(c *container.Container) {
|
||||||
msg := fmt.Sprintf("logging state for container: %s\n", c.Id)
|
msg := fmt.Sprintf("logging state for container: %s\n", c.Id)
|
||||||
|
|||||||
8
main.go
8
main.go
@@ -4,6 +4,7 @@ import (
|
|||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"runtime"
|
||||||
|
|
||||||
"github.com/bcicen/ctop/config"
|
"github.com/bcicen/ctop/config"
|
||||||
"github.com/bcicen/ctop/connector"
|
"github.com/bcicen/ctop/connector"
|
||||||
@@ -16,15 +17,16 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
build = "none"
|
build = "none"
|
||||||
version = "dev-build"
|
version = "dev-build"
|
||||||
|
goVersion = runtime.Version()
|
||||||
|
|
||||||
log *logging.CTopLogger
|
log *logging.CTopLogger
|
||||||
cursor *GridCursor
|
cursor *GridCursor
|
||||||
cGrid *compact.CompactGrid
|
cGrid *compact.CompactGrid
|
||||||
header *widgets.CTopHeader
|
header *widgets.CTopHeader
|
||||||
|
|
||||||
versionStr = fmt.Sprintf("ctop version %v, build %v", version, build)
|
versionStr = fmt.Sprintf("ctop version %v, build %v %v", version, build, goVersion)
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
@@ -1,4 +1,11 @@
|
|||||||
package metrics
|
package models
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Log struct {
|
||||||
|
Timestamp time.Time
|
||||||
|
Message string
|
||||||
|
}
|
||||||
|
|
||||||
type Metrics struct {
|
type Metrics struct {
|
||||||
CPUUtil int
|
CPUUtil int
|
||||||
@@ -24,10 +31,3 @@ func NewMetrics() Metrics {
|
|||||||
Pids: -1,
|
Pids: -1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Collector interface {
|
|
||||||
Stream() chan Metrics
|
|
||||||
Running() bool
|
|
||||||
Start()
|
|
||||||
Stop()
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user