Skip to content
This repository has been archived by the owner on Aug 29, 2020. It is now read-only.

Commit

Permalink
Log to file on errors
Browse files Browse the repository at this point in the history
- Also recover from issue #12
- Make config path management more portable
  • Loading branch information
cjbassi committed Dec 5, 2018
1 parent 3dee8bc commit c717178
Show file tree
Hide file tree
Showing 12 changed files with 148 additions and 74 deletions.
46 changes: 35 additions & 11 deletions main.go
Expand Up @@ -2,10 +2,11 @@ package main

import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"os"
"os/signal"
"path/filepath"
"sort"
"strconv"
"sync"
Expand All @@ -32,6 +33,8 @@ var (
widgetCount = 6
fahrenheit = false
configDir = getConfigDir()
logPath = filepath.Join(configDir, "errors.log")
stderrLogger = log.New(os.Stderr, "", 0)

cpu *w.CPU
mem *w.Mem
Expand Down Expand Up @@ -63,7 +66,10 @@ Colorschemes:
monokai
`

args, _ := docopt.ParseArgs(usage, os.Args[1:], version)
args, err := docopt.ParseArgs(usage, os.Args[1:], version)
if err != nil {
panic(err)
}

if val, _ := args["--color"]; val != nil {
handleColorscheme(val.(string))
Expand All @@ -77,7 +83,10 @@ Colorschemes:
}

rateStr, _ := args["--rate"].(string)
rate, _ := strconv.ParseFloat(rateStr, 64)
rate, err := strconv.ParseFloat(rateStr, 64)
if err != nil {
stderrLogger.Fatalf("error: invalid rate parameter")
}
if rate < 1 {
interval = time.Second * time.Duration(1/rate)
} else {
Expand All @@ -104,24 +113,22 @@ func handleColorscheme(cs string) {
func getConfigDir() string {
globalConfigDir := os.Getenv("XDG_CONFIG_HOME")
if globalConfigDir == "" {
globalConfigDir = os.ExpandEnv("$HOME") + "/.config"
globalConfigDir = filepath.Join(os.ExpandEnv("$HOME"), ".config")
}
return globalConfigDir + "/gotop"
return filepath.Join(globalConfigDir, "gotop")
}

// getCustomColorscheme tries to read a custom json colorscheme from {configDir}/{name}.json
func getCustomColorscheme(name string) colorschemes.Colorscheme {
filePath := configDir + "/" + name + ".json"
filePath := filepath.Join(configDir, name+".json")
dat, err := ioutil.ReadFile(filePath)
if err != nil {
fmt.Fprintf(os.Stderr, "error: colorscheme not recognized\n")
os.Exit(1)
stderrLogger.Fatalf("error: colorscheme not recognized")
}
var colorscheme colorschemes.Colorscheme
err = json.Unmarshal(dat, &colorscheme)
if err != nil {
fmt.Fprintf(os.Stderr, "error: could not parse colorscheme\n")
os.Exit(1)
stderrLogger.Fatalf("error: could not parse colorscheme")
}
return colorscheme
}
Expand Down Expand Up @@ -334,14 +341,31 @@ func eventLoop() {
}

func main() {
// make the config directory
err := os.MkdirAll(configDir, 0755)
if err != nil {
stderrLogger.Fatalf("failed to make the configuration directory: %v", err)
}
// open the log file
lf, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0660)
if err != nil {
stderrLogger.Fatalf("failed to open log file: %v", err)
}
defer lf.Close()

// log time, filename, and line number
log.SetFlags(log.Ltime | log.Lshortfile)
// log to file
log.SetOutput(lf)

cliArguments()
termuiColors() // need to do this before initializing widgets so that they can inherit the colors
initWidgets()
widgetColors()
help = w.NewHelpMenu()

// inits termui
err := ui.Init()
err = ui.Init()
if err != nil {
panic(err)
}
Expand Down
14 changes: 0 additions & 14 deletions src/utils/utils.go
@@ -1,10 +1,7 @@
package utils

import (
"fmt"
"math"

ui "github.com/cjbassi/termui"
)

var (
Expand Down Expand Up @@ -55,14 +52,3 @@ func Max(a, b int) int {
}
return b
}

func Error(issue, diagnostics string) {
ui.Close()
fmt.Println("Error caught. Exiting program.")
fmt.Println()
fmt.Println("Issue with " + issue + ".")
fmt.Println()
fmt.Println("Diagnostics:\n" + diagnostics)
fmt.Println()
panic(1)
}
27 changes: 14 additions & 13 deletions src/widgets/cpu.go
Expand Up @@ -2,9 +2,9 @@ package widgets

import (
"fmt"
"log"
"time"

"github.com/cjbassi/gotop/src/utils"
ui "github.com/cjbassi/termui"
psCPU "github.com/shirou/gopsutil/cpu"
)
Expand All @@ -19,7 +19,10 @@ type CPU struct {
}

func NewCPU(interval time.Duration, zoom int, average bool, percpu bool) *CPU {
count, _ := psCPU.Counts(false)
count, err := psCPU.Counts(false)
if err != nil {
log.Printf("failed to get CPU count from gopsutil: %v", err)
}
formatString := "CPU%1d"
if count > 10 {
formatString = "CPU%02d"
Expand Down Expand Up @@ -70,25 +73,23 @@ func NewCPU(interval time.Duration, zoom int, average bool, percpu bool) *CPU {
func (self *CPU) update() {
if self.Average {
go func() {
percent, _ := psCPU.Percent(self.interval, false)
percent, err := psCPU.Percent(self.interval, false)
if err != nil {
log.Printf("failed to get average CPU usage percent from gopsutil: %v. self.interval: %v. percpu: %v", err, self.interval, false)
}
self.Data["AVRG"] = append(self.Data["AVRG"], percent[0])
self.Labels["AVRG"] = fmt.Sprintf("%3.0f%%", percent[0])
}()
}

if self.PerCPU {
go func() {
percents, _ := psCPU.Percent(self.interval, true)
percents, err := psCPU.Percent(self.interval, true)
if err != nil {
log.Printf("failed to get CPU usage percents from gopsutil: %v. self.interval: %v. percpu: %v", err, self.interval, true)
}
if len(percents) != self.Count {
count, _ := psCPU.Counts(false)
utils.Error("CPU percentages",
fmt.Sprint(
"self.Count: ", self.Count, "\n",
"gopsutil.Counts(): ", count, "\n",
"len(percents): ", len(percents), "\n",
"percents: ", percents, "\n",
"self.interval: ", self.interval,
))
log.Printf("error: number of CPU usage percents from gopsutil doesn't match CPU count. percents: %v. self.Count: %v", percents, self.Count)
}
for i := 0; i < self.Count; i++ {
k := fmt.Sprintf(self.formatString, i)
Expand Down
16 changes: 13 additions & 3 deletions src/widgets/disk.go
Expand Up @@ -2,6 +2,7 @@ package widgets

import (
"fmt"
"log"
"sort"
"strings"
"time"
Expand Down Expand Up @@ -52,7 +53,10 @@ func NewDisk() *Disk {
}

func (self *Disk) update() {
Partitions, _ := psDisk.Partitions(false)
Partitions, err := psDisk.Partitions(false)
if err != nil {
log.Printf("failed to get disk partitions from gopsutil: %v", err)
}

// add partition if it's new
for _, Part := range Partitions {
Expand Down Expand Up @@ -93,13 +97,19 @@ func (self *Disk) update() {

// updates partition info
for _, Part := range self.Partitions {
usage, _ := psDisk.Usage(Part.Mount)
usage, err := psDisk.Usage(Part.Mount)
if err != nil {
log.Printf("failed to get partition usage statistics from gopsutil: %v. Part.Mount: %v", err, Part.Mount)
}
Part.UsedPercent = int(usage.UsedPercent)

Free, Mag := utils.ConvertBytes(usage.Free)
Part.Free = fmt.Sprintf("%3d%s", uint64(Free), Mag)

ret, _ := psDisk.IOCounters("/dev/" + Part.Device)
ret, err := psDisk.IOCounters("/dev/" + Part.Device)
if err != nil {
log.Printf("failed to get partition read/write info from gopsutil: %v. Part.Device: %v", err, Part.Device)
}
data := ret[Part.Device]
curRead, curWrite := data.ReadBytes, data.WriteBytes
if Part.TotalRead != 0 { // if this isn't the first update
Expand Down
11 changes: 9 additions & 2 deletions src/widgets/mem.go
Expand Up @@ -2,6 +2,7 @@ package widgets

import (
"fmt"
"log"
"time"

"github.com/cjbassi/gotop/src/utils"
Expand Down Expand Up @@ -37,8 +38,14 @@ func NewMem(interval time.Duration, zoom int) *Mem {
}

func (self *Mem) update() {
main, _ := psMem.VirtualMemory()
swap, _ := psMem.SwapMemory()
main, err := psMem.VirtualMemory()
if err != nil {
log.Printf("failed to get main memory info from gopsutil: %v", err)
}
swap, err := psMem.SwapMemory()
if err != nil {
log.Printf("failed to get swap memory info from gopsutil: %v", err)
}
self.Data["Main"] = append(self.Data["Main"], main.UsedPercent)
self.Data["Swap"] = append(self.Data["Swap"], swap.UsedPercent)

Expand Down
31 changes: 16 additions & 15 deletions src/widgets/net.go
Expand Up @@ -2,6 +2,7 @@ package widgets

import (
"fmt"
"log"
"time"

"github.com/cjbassi/gotop/src/utils"
Expand Down Expand Up @@ -45,7 +46,10 @@ func NewNet() *Net {

func (self *Net) update() {
// `false` causes psutil to group all network activity
interfaces, _ := psNet.IOCounters(false)
interfaces, err := psNet.IOCounters(false)
if err != nil {
log.Printf("failed to get network activity from gopsutil: %v", err)
}
curRecvTotal := interfaces[0].BytesRecv
curSentTotal := interfaces[0].BytesSent
var recvRecent uint64
Expand All @@ -55,22 +59,19 @@ func (self *Net) update() {
recvRecent = curRecvTotal - self.prevRecvTotal
sentRecent = curSentTotal - self.prevSentTotal

if int(recvRecent) < 0 {
log.Printf("error: negative value for recently received network data from gopsutil. recvRecent: %v", recvRecent)
// recover from error
recvRecent = 0
}
if int(sentRecent) < 0 {
log.Printf("error: negative value for recently sent network data from gopsutil. sentRecent: %v", sentRecent)
// recover from error
sentRecent = 0
}

self.Lines[0].Data = append(self.Lines[0].Data, int(recvRecent))
self.Lines[1].Data = append(self.Lines[1].Data, int(sentRecent))

if int(recvRecent) < 0 || int(sentRecent) < 0 {
utils.Error("net data",
fmt.Sprint(
"curRecvTotal: ", curRecvTotal, "\n",
"curSentTotal: ", curSentTotal, "\n",
"self.prevRecvTotal: ", self.prevRecvTotal, "\n",
"self.prevSentTotal: ", self.prevSentTotal, "\n",
"recvRecent: ", recvRecent, "\n",
"sentRecent: ", sentRecent, "\n",
"int(recvRecent): ", int(recvRecent), "\n",
"int(sentRecent): ", int(sentRecent),
))
}
}

// used in later calls to update
Expand Down
6 changes: 5 additions & 1 deletion src/widgets/proc.go
Expand Up @@ -2,6 +2,7 @@ package widgets

import (
"fmt"
"log"
"os/exec"
"sort"
"strconv"
Expand Down Expand Up @@ -37,7 +38,10 @@ type Proc struct {
}

func NewProc() *Proc {
cpuCount, _ := psCPU.Counts(false)
cpuCount, err := psCPU.Counts(false)
if err != nil {
log.Printf("failed to get CPU count from gopsutil: %v", err)
}
self := &Proc{
Table: ui.NewTable(),
interval: time.Second,
Expand Down
23 changes: 18 additions & 5 deletions src/widgets/proc_unix.go
Expand Up @@ -3,6 +3,7 @@
package widgets

import (
"log"
"os/exec"
"strconv"
"strings"
Expand All @@ -11,7 +12,7 @@ import (
func (self *Proc) update() {
processes := Processes()
// have to iterate like this in order to actually change the value
for i, _ := range processes {
for i := range processes {
processes[i].CPU /= self.cpuCount
}

Expand All @@ -22,15 +23,27 @@ func (self *Proc) update() {
}

func Processes() []Process {
output, _ := exec.Command("ps", "-axo", "pid,comm,pcpu,pmem,args").Output()
output, err := exec.Command("ps", "-axo", "pid,comm,pcpu,pmem,args").Output()
if err != nil {
log.Printf("failed to execute 'ps' command: %v", err)
}
// converts to []string and removes the header
strOutput := strings.Split(strings.TrimSpace(string(output)), "\n")[1:]
processes := []Process{}
for _, line := range strOutput {
split := strings.Fields(line)
pid, _ := strconv.Atoi(split[0])
cpu, _ := strconv.ParseFloat(split[2], 64)
mem, _ := strconv.ParseFloat(split[3], 64)
pid, err := strconv.Atoi(split[0])
if err != nil {
log.Printf("failed to convert first field to int: %v. split: %v", err, split)
}
cpu, err := strconv.ParseFloat(split[2], 64)
if err != nil {
log.Printf("failed to convert third field to float: %v. split: %v", err, split)
}
mem, err := strconv.ParseFloat(split[3], 64)
if err != nil {
log.Printf("failed to convert fourth field to float: %v. split: %v", err, split)
}
process := Process{
PID: pid,
Command: split[1],
Expand Down

0 comments on commit c717178

Please sign in to comment.