summaryrefslogtreecommitdiffstats
path: root/logging/logging.go
blob: c79b5c8647979018c1f90ce010760350954d059a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
package logging

import (
	"fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	"sync"

	"github.com/xxxserxxx/gotop/v3"
)

const (
	LOGFILE = "errors.log"
)

func New(c gotop.Config) (io.WriteCloser, error) {
	// create the log directory
	cache := c.ConfigDir.QueryCacheFolder()
	err := cache.MkdirAll()
	if err != nil && !os.IsExist(err) {
		return nil, err
	}
	w := &RotateWriter{
		filename:   filepath.Join(cache.Path, 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
}