Fixes #49, logs filling up hard drive.
This commit is contained in:
parent
774b4fcda6
commit
a007047fc5
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -5,6 +5,6 @@ import (
|
|||
"syscall"
|
||||
)
|
||||
|
||||
func StderrToLogfile(logfile *os.File) {
|
||||
func stderrToLogfile(logfile *os.File) {
|
||||
syscall.Dup3(int(logfile.Fd()), 2, 0)
|
||||
}
|
||||
|
|
|
@ -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
logging/logging_test.go
Normal file
55
logging/logging_test.go
Normal file
|
@ -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))
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user