-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathdiskwriter.go
More file actions
158 lines (131 loc) · 3.42 KB
/
diskwriter.go
File metadata and controls
158 lines (131 loc) · 3.42 KB
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
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package logmanager
import (
"fmt"
"io"
"os"
"path"
"path/filepath"
"time"
)
// DiskWriterConfig ...
type DiskWriterConfig struct {
RotateDuration time.Duration // rotates after time.Duration since log file creation date
MaximumLogFiles int
}
// DiskWriter ...
type DiskWriter struct {
DiskWriterConfig
logpath string
logbuf chan string
}
// rotateLogs will rotate the current logs and return the next rotation time
func (w DiskWriter) rotateLogs() (time.Time, error) {
if _, err := os.Stat(w.logpath); err != nil {
// no log to rotate yet
_ = os.MkdirAll(path.Dir(w.logpath), 0777)
f, err := os.Create(w.logpath)
if err != nil {
return time.Time{}, err
}
defer f.Close()
return time.Now().Add(w.RotateDuration), nil
}
logfiles := []string{path.Base(w.logpath)}
logDir := path.Dir(w.logpath)
// find all the log files that currently exist
for i := 1; ; i++ {
logfilename := fmt.Sprintf("%s.%d", path.Base(w.logpath), i)
logpath := path.Join(logDir, logfilename)
_, err := os.Stat(logpath)
if err != nil {
break
}
logfiles = append(logfiles, logfilename)
}
// reverse traverse the list so we can rename foo.log.3 before foo.log.2
for i := len(logfiles) - 1; i >= 0; i-- {
oldName := logfiles[i]
newName := fmt.Sprintf("%s.%d", path.Base(w.logpath), i+1)
_ = os.Rename(path.Join(logDir, oldName), path.Join(logDir, newName))
if i >= w.MaximumLogFiles-1 {
os.Remove(path.Join(logDir, newName))
}
}
_ = os.MkdirAll(path.Dir(w.logpath), 0777)
f, err := os.Create(w.logpath)
if err != nil {
return time.Time{}, err
}
defer f.Close()
return time.Now().Add(w.RotateDuration), nil
}
func writeAll(writer io.Writer, buf []byte) error {
for len(buf) > 0 {
n, err := writer.Write(buf)
buf = buf[n:]
if err != nil {
return err
}
}
return nil
}
// NewDiskWriter ...
func NewDiskWriter(logpath string, config DiskWriterConfig) *DiskWriter {
w := &DiskWriter{config, logpath, make(chan string, 10000)}
go func() {
var err error
var file *os.File
rotateTime := time.Time{}
for logLine := range w.logbuf {
// rotate the logs if it has been longer than w.RotateDuration since last rotation
if time.Now().After(rotateTime) {
rotateTime, err = w.rotateLogs()
if err != nil {
println("Warning, could not create logfile:", err.Error())
continue
}
if file != nil {
file.Close()
file = nil
}
}
if file == nil {
file, err = os.OpenFile(w.logpath, os.O_APPEND|os.O_WRONLY, os.ModeAppend)
if err != nil {
println("Warning, could not open logfile for appending:", err.Error())
continue
}
}
// write into the log file
err = writeAll(file, []byte(logLine))
if err != nil {
println("Warning, Error writing logfile:", err.Error())
}
}
if file != nil {
file.Close()
}
}()
return w
}
// Close will end the writer
func (w *DiskWriter) Close() {
close(w.logbuf)
}
// BuildTheme ...
func (w *DiskWriter) BuildTheme(string) ColorTheme {
return ColorTheme{}
}
// Log ...
func (w *DiskWriter) Log(level Level, _ ColorTheme, module, filename string, line int, timestamp time.Time, message string) {
if level <= Debug {
return
}
ts := timestamp.In(time.UTC).Format("15:04:05")
filename = filepath.Base(filename)
select {
case w.logbuf <- fmt.Sprintf("%s %s %s %s:%d %s\n", ts, level.String(), module, filename, line, message):
default:
println("WARNING: could not log to logfile, buffer full")
}
}