summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSean E. Russell <ser@ser1.net>2020-02-15 14:27:31 -0600
committerSean E. Russell <ser@ser1.net>2020-02-15 14:27:31 -0600
commita007047fc551abdcebe85a6240f508ed120867bc (patch)
tree298755b1339d73273f5ec52621129c9bd2f34e42
parent774b4fcda6a7c0bd956b0396fe0c4f77bce3a76d (diff)
Fixes #49, logs filling up hard drive.
-rw-r--r--CHANGELOG.md7
-rw-r--r--cmd/gotop/main.go26
-rw-r--r--config.go3
-rw-r--r--logging/logging_arm64.go2
-rw-r--r--logging/logging_other.go97
-rw-r--r--logging/logging_test.go55
6 files changed, 164 insertions, 26 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 78c81bd..14b710f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
> - **Fixed**: for any bug fixes.
> - **Security**: in case of vulnerabilities.
+## [3.3.0] -
+
+- Logs are now rotated. Settings are currently hard-coded at 4 files of 5MB
+ each, so logs shouldn't take up more than 20MB. I'm going to see how many
+ complain about wanting to configure these settings before I add code to do
+ that.
+
## [3.2.0] - 2020-02-14
Bug fixes & pull requests
diff --git a/cmd/gotop/main.go b/cmd/gotop/main.go
index d3384b0..eb2364f 100644
--- a/cmd/gotop/main.go
+++ b/cmd/gotop/main.go
@@ -79,7 +79,7 @@ Colorschemes:
conf = gotop.Config{
ConfigDir: cd,
LogDir: ld,
- LogPath: filepath.Join(ld, "errors.log"),
+ LogFile: "errors.log",
GraphHorizontalScale: 7,
HelpVisible: false,
Colorscheme: colorschemes.Default,
@@ -90,6 +90,7 @@ Colorschemes:
Battery: false,
Statusbar: false,
NetInterface: w.NET_INTERFACE_ALL,
+ MaxLogSize: 5000000,
}
args, err := docopt.ParseArgs(usage, os.Args[1:], version)
@@ -376,32 +377,13 @@ func eventLoop(c gotop.Config, grid *layout.MyGrid) {
}
}
-func setupLogfile(c gotop.Config) (*os.File, error) {
- // create the log directory
- if err := os.MkdirAll(c.LogDir, 0755); err != nil {
- return nil, fmt.Errorf("failed to make the log directory: %v", err)
- }
- // open the log file
- logfile, err := os.OpenFile(c.LogPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0660)
- if err != nil {
- return nil, fmt.Errorf("failed to open log file: %v", err)
- }
-
- // log time, filename, and line number
- log.SetFlags(log.Ltime | log.Lshortfile)
- // log to file
- log.SetOutput(logfile)
-
- return logfile, nil
-}
-
func main() {
conf, err := parseArgs()
if err != nil {
stderrLogger.Fatalf("failed to parse cli args: %v", err)
}
- logfile, err := setupLogfile(conf)
+ logfile, err := logging.New(conf)
if err != nil {
stderrLogger.Fatalf("failed to setup log file: %v", err)
}
@@ -412,8 +394,6 @@ func main() {
}
defer ui.Close()
- logging.StderrToLogfile(logfile)
-
setDefaultTermuiColors(conf) // done before initializing widgets to allow inheriting colors
help = w.NewHelpMenu()
if statusbar {
diff --git a/config.go b/config.go
index c2e4cc5..a400539 100644
--- a/config.go
+++ b/config.go
@@ -18,7 +18,7 @@ import (
type Config struct {
ConfigDir string
LogDir string
- LogPath string
+ LogFile string
GraphHorizontalScale int
HelpVisible bool
@@ -32,4 +32,5 @@ type Config struct {
Statusbar bool
NetInterface string
Layout io.Reader
+ MaxLogSize int64
}
diff --git a/logging/logging_arm64.go b/logging/logging_arm64.go
index 2b7a25f..48ace31 100644
--- a/logging/logging_arm64.go
+++ b/logging/logging_arm64.go
@@ -5,6 +5,6 @@ import (
"syscall"
)
-func StderrToLogfile(logfile *os.File) {
+func stderrToLogfile(logfile *os.File) {
syscall.Dup3(int(logfile.Fd()), 2, 0)
}
diff --git a/logging/logging_other.go b/logging/logging_other.go
index 382baca..b102e29 100644
--- a/logging/logging_other.go
+++ b/logging/logging_other.go
@@ -3,10 +3,105 @@
package logging
import (
+ "fmt"
+ "io"
+ "log"
"os"
+ "path/filepath"
+ "sync"
"syscall"
+
+ "github.com/xxxserxxx/gotop"
)
-func StderrToLogfile(logfile *os.File) {
+func stderrToLogfile(logfile *os.File) {
syscall.Dup2(int(logfile.Fd()), 2)
}
+
+func New(c gotop.Config) (io.WriteCloser, error) {
+ // create the log directory
+ if err := os.MkdirAll(c.LogDir, 0755); err != nil {
+ return nil, fmt.Errorf("failed to make the log directory: %v", err)
+ }
+ w := &RotateWriter{
+ filename: filepath.Join(c.LogDir, c.LogFile),
+ maxLogSize: c.MaxLogSize,
+ }
+ err := w.rotate()
+ if err != nil {
+ return nil, err
+ }
+ // log time, filename, and line number
+ log.SetFlags(log.Ltime | log.Lshortfile)
+ // log to file
+ log.SetOutput(w)
+
+ stderrToLogfile(w.fp)
+ return w, nil
+}
+
+type RotateWriter struct {
+ lock sync.Mutex
+ filename string // should be set to the actual filename
+ fp *os.File
+ maxLogSize int64
+}
+
+func (w *RotateWriter) Close() error {
+ return w.fp.Close()
+}
+
+// Write satisfies the io.Writer interface.
+func (w *RotateWriter) Write(output []byte) (int, error) {
+ w.lock.Lock()
+ defer w.lock.Unlock()
+ // Rotate if the log hits the size limit
+ s, err := os.Stat(w.filename)
+ if err == nil {
+ if s.Size() > w.maxLogSize {
+ w.rotate()
+ }
+ }
+ return w.fp.Write(output)
+}
+
+// Perform the actual act of rotating and reopening file.
+func (w *RotateWriter) rotate() (err error) {
+ // Close existing file if open
+ if w.fp != nil {
+ err = w.fp.Close()
+ w.fp = nil
+ if err != nil {
+ return
+ }
+ }
+ // This will keep three logs
+ for i := 1; i > -1; i-- {
+ from := fmt.Sprintf("%s.%d", w.filename, i)
+ to := fmt.Sprintf("%s.%d", w.filename, i+1)
+ // Rename dest file if it already exists
+ _, err = os.Stat(from)
+ if err == nil {
+ err = os.Rename(from, to)
+ if err != nil {
+ return
+ }
+ }
+ }
+ // Rename dest file if it already exists
+ _, err = os.Stat(w.filename)
+ if err == nil {
+ err = os.Rename(w.filename, fmt.Sprintf("%s.%d", w.filename, 0))
+ if err != nil {
+ return
+ }
+ }
+
+ // open the log file
+ w.fp, err = os.OpenFile(w.filename, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0660)
+ if err != nil {
+ return fmt.Errorf("failed to open log file %s: %v", w.filename, err)
+ }
+
+ return nil
+}
diff --git a/logging/logging_test.go b/logging/logging_test.go
new file mode 100644
index 0000000..3bff162
--- /dev/null
+++ b/logging/logging_test.go
@@ -0,0 +1,55 @@
+package logging
+
+import (
+ "io/ioutil"
+ //"log"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+ "github.com/xxxserxxx/gotop"
+)
+
+func TestLogging(t *testing.T) {
+ tdn := "testdir"
+ path, err := filepath.Abs(tdn)
+ defer os.RemoveAll(path)
+ c := gotop.Config{
+ MaxLogSize: 300,
+ LogDir: path,
+ LogFile: "errors.log",
+ }
+ wc, err := New(c)
+ assert.NoError(t, err)
+ if err != nil {
+ return
+ }
+ defer wc.Close()
+ ds := make([]byte, 100)
+ for i, _ := range ds {
+ ds[i] = 'x'
+ }
+
+ // Base case -- empty log file
+ td, err := ioutil.ReadDir(path)
+ assert.NoError(t, err)
+ if err != nil {
+ return
+ }
+ assert.Equal(t, 1, len(td))
+
+ for i := 1; i < 6; i++ {
+ wc.Write(ds)
+ wc.Write(ds)
+ wc.Write(ds)
+ wc.Write([]byte{'\n'}) // max... + 1
+ td, err = ioutil.ReadDir(path)
+ assert.NoError(t, err)
+ k := i
+ if k > 4 {
+ k = 4
+ }
+ assert.Equal(t, k, len(td))
+ }
+}