2020-02-16 04:57:18 +08:00
|
|
|
package logging
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"log"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"sync"
|
|
|
|
|
2020-06-21 05:47:48 +08:00
|
|
|
"github.com/jdkeke142/lingo-toml"
|
2020-04-24 03:07:08 +08:00
|
|
|
"github.com/xxxserxxx/gotop/v4"
|
2020-02-16 04:57:18 +08:00
|
|
|
)
|
|
|
|
|
2020-03-07 23:03:25 +08:00
|
|
|
const (
|
|
|
|
LOGFILE = "errors.log"
|
|
|
|
)
|
|
|
|
|
2020-06-08 05:40:03 +08:00
|
|
|
// New creates a new logger in the default cache directory; the returned
|
|
|
|
// WriteCloser should be closed when the program exits. If an error is
|
|
|
|
// encountered during file creation, a nil WriteCloser and appropriate
|
|
|
|
// error are returned.
|
2020-02-16 04:57:18 +08:00
|
|
|
func New(c gotop.Config) (io.WriteCloser, error) {
|
|
|
|
// create the log directory
|
2020-03-07 23:03:25 +08:00
|
|
|
cache := c.ConfigDir.QueryCacheFolder()
|
|
|
|
err := cache.MkdirAll()
|
|
|
|
if err != nil && !os.IsExist(err) {
|
|
|
|
return nil, err
|
2020-02-16 04:57:18 +08:00
|
|
|
}
|
|
|
|
w := &RotateWriter{
|
2020-03-07 23:03:25 +08:00
|
|
|
filename: filepath.Join(cache.Path, LOGFILE),
|
2020-02-16 04:57:18 +08:00
|
|
|
maxLogSize: c.MaxLogSize,
|
2020-06-21 05:47:48 +08:00
|
|
|
tr: c.Tr,
|
2020-02-16 04:57:18 +08:00
|
|
|
}
|
2020-03-07 23:03:25 +08:00
|
|
|
err = w.rotate()
|
2020-02-16 04:57:18 +08:00
|
|
|
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
|
2020-06-21 05:47:48 +08:00
|
|
|
tr lingo.Translations
|
2020-02-16 04:57:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2020-06-21 05:47:48 +08:00
|
|
|
return fmt.Errorf(w.tr.Value("error.logopen", w.filename, err.Error()))
|
2020-02-16 04:57:18 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|