Extensions.

This commit is contained in:
Sean E. Russell 2020-02-28 10:03:41 -06:00
parent 7a89225d72
commit 231b0d03fe
17 changed files with 102 additions and 41 deletions

View File

@ -8,6 +8,7 @@ import (
"os"
"os/signal"
"path/filepath"
"plugin"
"strconv"
"strings"
"syscall"
@ -33,6 +34,7 @@ const (
defaultUI = "cpu\ndisk/1 2:mem/2\ntemp\nnet procs"
minimalUI = "cpu\nmem procs"
batteryUI = "cpu/2 batt/1\ndisk/1 2:mem/2\ntemp\nnet procs"
procsUI = "cpu 4:procs\ndisk\nmem\nnet"
)
var (
@ -65,12 +67,16 @@ Options:
-b, --battery Show battery level widget ('minimal' turns off). (DEPRECATED, use -l battery)
-B, --bandwidth=bits Specify the number of bits per seconds.
-l, --layout=NAME Name of layout spec file for the UI. Looks first in $XDG_CONFIG_HOME/gotop, then as a path. Use "-" to pipe.
-i, --interface=NAME Select network interface [default: all].
-i, --interface=NAME Select network interface [default: all]. Several interfaces can be defined using comma separated values. Interfaces can also be ignored using !
-x, --export=PORT Enable metrics for export on the specified port.
-X, --extensions=NAMES Enables the listed extensions. This is a comma-separated list without the .so suffix. The current and config directories will be searched.
Several interfaces can be defined using comma separated values.
Interfaces can also be ignored using !
Built-in layouts:
default
minimal
battery
kitchensink
Colorschemes:
default
@ -142,6 +148,10 @@ Colorschemes:
if val, _ := args["--interface"]; val != nil {
conf.NetInterface, _ = args["--interface"].(string)
}
if val, _ := args["--extensions"]; val != nil {
exs, _ := args["--extensions"].(string)
conf.Extensions = strings.Split(exs, ",")
}
return nil
}
@ -386,6 +396,8 @@ func main() {
bar = w.NewStatusBar()
}
loadExtensions(conf)
lstream := getLayout(conf)
ly := layout.ParseLayout(lstream)
grid, err := layout.Layout(ly, conf)
@ -426,6 +438,8 @@ func getLayout(conf gotop.Config) io.Reader {
return strings.NewReader(minimalUI)
case "battery":
return strings.NewReader(batteryUI)
case "procs":
return strings.NewReader(procsUI)
default:
fp := filepath.Join(conf.ConfigDir, conf.Layout)
fin, err := os.Open(fp)
@ -438,3 +452,47 @@ func getLayout(conf gotop.Config) io.Reader {
return fin
}
}
func loadExtensions(conf gotop.Config) {
var hasError bool
for _, ex := range conf.Extensions {
exf := ex + ".so"
fn := exf
_, err := os.Stat(fn)
if err != nil && os.IsNotExist(err) {
log.Printf("no plugin %s found in current directory", fn)
fn = filepath.Join(conf.ConfigDir, exf)
_, err = os.Stat(fn)
if err != nil || os.IsNotExist(err) {
hasError = true
log.Printf("no plugin %s found in config directory", fn)
continue
}
}
p, err := plugin.Open(fn)
if err != nil {
hasError = true
log.Printf(err.Error())
continue
}
init, err := p.Lookup("Init")
if err != nil {
hasError = true
log.Printf(err.Error())
continue
}
initFunc, ok := init.(func())
if !ok {
hasError = true
log.Printf(err.Error())
continue
}
initFunc()
}
if hasError {
ui.Close()
fmt.Printf("Error initializing requested plugins; check the log file %s\n", filepath.Join(conf.ConfigDir, conf.LogFile))
os.Exit(1)
}
}

View File

@ -12,8 +12,7 @@ func init() {
BattLines: []int{4, 3, 2, 1, 5, 6, 7, 8},
MainMem: 5,
SwapMem: 11,
MemLines: []int{5, 11, 4, 3, 2, 1, 6, 7, 8},
ProcCursor: 4,

View File

@ -11,8 +11,7 @@
"BattLines": [4, 3, 2, 1, 5, 6, 7, 8],
"MainMem": 5,
"SwapMem": 11,
"MemLines": [5, 11, 4, 3, 2, 1, 6, 7, 8],
"ProcCursor": 4,

View File

@ -12,8 +12,7 @@ func init() {
BattLines: []int{4, 3, 2, 1, 5, 6, 7, 8},
MainMem: 5,
SwapMem: 3,
MemLines: []int{5, 3, 4, 2, 1, 6, 7, 8, 11},
ProcCursor: 33,

View File

@ -12,8 +12,7 @@ func init() {
BattLines: []int{81, 70, 208, 197, 249, 141, 221, 186},
MainMem: 208,
SwapMem: 186,
MemLines: []int{208, 186, 81, 70, 208, 197, 249, 141, 221, 186},
ProcCursor: 197,

View File

@ -25,8 +25,7 @@ func init() {
BattLines: []int{4, 3, 2, 1, 5, 6, 7, 8},
MainMem: 172, // Orange
SwapMem: 221, // yellow
MemLines: []int{172, 221, 4, 3, 2, 1, 5, 6, 7, 8},
ProcCursor: 31, // blue (nord9)

View File

@ -15,8 +15,7 @@ func init() {
BattLines: []int{61, 33, 37, 64, 125, 160, 166, 136},
MainMem: 125,
SwapMem: 166,
MemLines: []int{125, 166, 61, 33, 37, 64, 125, 160, 166, 136},
ProcCursor: 136,

View File

@ -14,8 +14,7 @@ func init() {
BattLines: []int{13, 4, 6, 2, 5, 1, 9, 3},
MainMem: 5,
SwapMem: 9,
MemLines: []int{5, 9, 13, 4, 6, 2, 1, 3},
ProcCursor: 4,

View File

@ -14,8 +14,7 @@ func init() {
BattLines: []int{13, 4, 6, 2, 5, 1, 9, 3},
MainMem: 5,
SwapMem: 9,
MemLines: []int{5, 9, 13, 4, 6, 2, 1, 3},
ProcCursor: 4,

View File

@ -32,8 +32,7 @@ type Colorscheme struct {
BattLines []int
MainMem int
SwapMem int
MemLines []int
ProcCursor int

View File

@ -12,8 +12,7 @@ func init() {
BattLines: []int{212, 218, 123, 159, 229, 158, 183, 146},
MainMem: 201,
SwapMem: 97,
MemLines: []int{201, 97, 212, 218, 123, 159, 229, 158, 183, 146},
ProcCursor: 159,

View File

@ -31,6 +31,7 @@ type Config struct {
Layout string
MaxLogSize int64
ExportPort string
Extensions []string
}
func Parse(in io.Reader, conf *Config) error {
@ -112,6 +113,8 @@ func Parse(in io.Reader, conf *Config) error {
conf.MaxLogSize = int64(iv)
case "export":
conf.ExportPort = kv[1]
case "extensions":
conf.Extensions = strings.Split(kv[1], ",")
}
}

View File

@ -5,7 +5,7 @@ import (
"time"
)
var cpuFuncs []func(map[string]float64, time.Duration, bool) map[string]error
var cpuFuncs []func(map[string]int, time.Duration, bool) map[string]error
// RegisterCPU adds a new CPU device to the CPU widget. labels returns the
// names of the devices; they should be as short as possible, and the indexes
@ -16,13 +16,13 @@ var cpuFuncs []func(map[string]float64, time.Duration, bool) map[string]error
//
// labels may be called once and the value cached. This means the number of
// cores should not change dynamically.
func RegisterCPU(f func(map[string]float64, time.Duration, bool) map[string]error) {
func RegisterCPU(f func(map[string]int, time.Duration, bool) map[string]error) {
cpuFuncs = append(cpuFuncs, f)
}
// CPUPercent calculates the percentage of cpu used either per CPU or combined.
// Returns one value per cpu, or a single value if percpu is set to false.
func UpdateCPU(cpus map[string]float64, interval time.Duration, logical bool) {
func UpdateCPU(cpus map[string]int, interval time.Duration, logical bool) {
for _, f := range cpuFuncs {
errs := f(cpus, interval, logical)
if errs != nil {

View File

@ -8,7 +8,7 @@ import (
)
func init() {
f := func(cpus map[string]float64, iv time.Duration, l bool) map[string]error {
f := func(cpus map[string]int, iv time.Duration, l bool) map[string]error {
cpuCount, err := psCpu.Counts(l)
if err != nil {
return nil
@ -23,7 +23,7 @@ func init() {
}
for i := 0; i < len(vals); i++ {
key := fmt.Sprintf(formatString, i)
cpus[key] = vals[i]
cpus[key] = int(vals[i])
}
return nil
}

View File

@ -168,8 +168,15 @@ func makeWidget(c gotop.Config, widRule widgetRule) interface{} {
w = dw
case "mem":
m := widgets.NewMemWidget(c.UpdateInterval, c.GraphHorizontalScale)
m.LineColors["Main"] = ui.Color(c.Colorscheme.MainMem)
m.LineColors["Swap"] = ui.Color(c.Colorscheme.SwapMem)
var i int
for key, _ := range m.Data {
if i >= len(c.Colorscheme.MemLines) {
i = 0
}
color := c.Colorscheme.MemLines[i]
m.LineColors[key] = ui.Color(color)
i++
}
w = m
case "temp":
t := widgets.NewTempWidget(c.TempScale)

View File

@ -48,10 +48,10 @@ func NewCpuWidget(updateInterval time.Duration, horizontalScale int, showAverage
}
if self.ShowPerCpuLoad {
cpus := make(map[string]float64)
cpus := make(map[string]int)
devices.UpdateCPU(cpus, self.updateInterval, self.ShowPerCpuLoad)
for k, v := range cpus {
self.Data[k] = []float64{v}
self.Data[k] = []float64{float64(v)}
}
}
@ -74,7 +74,7 @@ func (self *CpuWidget) EnableMetric() {
Name: "avg",
})
} else {
cpus := make(map[string]float64)
cpus := make(map[string]int)
devices.UpdateCPU(cpus, self.updateInterval, self.ShowPerCpuLoad)
self.metric = make(map[string]prometheus.Gauge)
for key, perc := range cpus {
@ -83,7 +83,7 @@ func (self *CpuWidget) EnableMetric() {
Subsystem: "cpu",
Name: key,
})
gauge.Set(perc)
gauge.Set(float64(perc))
prometheus.MustRegister(gauge)
self.metric[key] = gauge
}
@ -97,7 +97,7 @@ func (b *CpuWidget) Scale(i int) {
func (self *CpuWidget) update() {
if self.ShowAverageLoad {
go func() {
cpus := make(map[string]float64)
cpus := make(map[string]int)
devices.UpdateCPU(cpus, self.updateInterval, false)
self.Lock()
defer self.Unlock()
@ -105,7 +105,7 @@ func (self *CpuWidget) update() {
defer self.updateLock.Unlock()
var val float64
for _, v := range cpus {
val = v
val = float64(v)
break
}
self.Data["AVRG"] = append(self.Data["AVRG"], val)
@ -118,20 +118,20 @@ func (self *CpuWidget) update() {
if self.ShowPerCpuLoad {
go func() {
cpus := make(map[string]float64)
cpus := make(map[string]int)
devices.UpdateCPU(cpus, self.updateInterval, true)
self.Lock()
defer self.Unlock()
self.updateLock.Lock()
defer self.updateLock.Unlock()
for key, percent := range cpus {
self.Data[key] = append(self.Data[key], percent)
self.Labels[key] = fmt.Sprintf("%3.0f%%", percent)
self.Data[key] = append(self.Data[key], float64(percent))
self.Labels[key] = fmt.Sprintf("%d%%", percent)
if self.metric != nil {
if self.metric[key] == nil {
log.Printf("no metrics for %s", key)
} else {
self.metric[key].Set(percent)
self.metric[key].Set(float64(percent))
}
}
}

View File

@ -2,6 +2,7 @@ package widgets
import (
"fmt"
"log"
"time"
"github.com/prometheus/client_golang/prometheus"
@ -27,7 +28,8 @@ func NewMemWidget(updateInterval time.Duration, horizontalScale int) *MemWidget
mems := make(map[string]devices.MemoryInfo)
devices.UpdateMem(mems)
for name, mem := range mems {
self.Data[name] = []float64{mem.UsedPercent}
log.Printf("setting %s to %v", name, mem)
self.Data[name] = []float64{0}
self.renderMemInfo(name, mem)
}
@ -36,6 +38,7 @@ func NewMemWidget(updateInterval time.Duration, horizontalScale int) *MemWidget
self.Lock()
devices.UpdateMem(mems)
for label, mi := range mems {
log.Printf(" updating %s to %v", label, mi)
self.renderMemInfo(label, mi)
if self.metrics != nil && self.metrics[label] != nil {
self.metrics[label].Set(mi.UsedPercent)