Skip to content

Commit

Permalink
Fixes #49, logs filling up hard drive.
Browse files Browse the repository at this point in the history
  • Loading branch information
xxxserxxx committed Feb 15, 2020
1 parent 774b4fc commit a007047
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 26 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
26 changes: 3 additions & 23 deletions cmd/gotop/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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)
Expand Down Expand Up @@ -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)
}
Expand All @@ -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 {
Expand Down
3 changes: 2 additions & 1 deletion config.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
type Config struct {
ConfigDir string
LogDir string
LogPath string
LogFile string

GraphHorizontalScale int
HelpVisible bool
Expand All @@ -32,4 +32,5 @@ type Config struct {
Statusbar bool
NetInterface string
Layout io.Reader
MaxLogSize int64
}
2 changes: 1 addition & 1 deletion logging/logging_arm64.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ import (
"syscall"
)

func StderrToLogfile(logfile *os.File) {
func stderrToLogfile(logfile *os.File) {
syscall.Dup3(int(logfile.Fd()), 2, 0)
}
97 changes: 96 additions & 1 deletion logging/logging_other.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
55 changes: 55 additions & 0 deletions logging/logging_test.go
Original file line number Diff line number Diff line change
@@ -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))
}
}

0 comments on commit a007047

Please sign in to comment.