From a007047fc551abdcebe85a6240f508ed120867bc Mon Sep 17 00:00:00 2001 From: "Sean E. Russell" Date: Sat, 15 Feb 2020 14:27:31 -0600 Subject: Fixes #49, logs filling up hard drive. --- logging/logging_arm64.go | 2 +- logging/logging_other.go | 97 +++++++++++++++++++++++++++++++++++++++++++++++- logging/logging_test.go | 55 +++++++++++++++++++++++++++ 3 files changed, 152 insertions(+), 2 deletions(-) create mode 100644 logging/logging_test.go (limited to 'logging') 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)) + } +} -- cgit v1.2.3