forked from arthurkiller/rollingwriter
-
Notifications
You must be signed in to change notification settings - Fork 0
/
manager.go
141 lines (127 loc) · 3.05 KB
/
manager.go
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
package rollingwriter
import (
"os"
"path"
"strconv"
"strings"
"sync"
"time"
"github.com/robfig/cron"
)
type manager struct {
thresholdSize int64
startAt time.Time
fire chan string
cr *cron.Cron
context chan int
wg sync.WaitGroup
lock sync.Mutex
}
// NewManager generate the Manager with config
func NewManager(c *Config) (Manager, error) {
m := &manager{
startAt: time.Now(),
cr: cron.New(),
fire: make(chan string),
context: make(chan int),
wg: sync.WaitGroup{},
}
// start the manager according to policy
switch c.RollingPolicy {
default:
fallthrough
case WithoutRolling:
return m, nil
case TimeRolling:
if err := m.cr.AddFunc(c.RollingTimePattern, func() {
m.fire <- m.GenLogFileName(c)
}); err != nil {
return nil, err
}
m.cr.Start()
case VolumeRolling:
m.ParseVolume(c)
m.wg.Add(1)
go func() {
timer := time.Tick(time.Duration(Precision) * time.Second)
filepath := LogFilePath(c)
var file *os.File
var err error
m.wg.Done()
for {
select {
case <-m.context:
return
case <-timer:
if file, err = os.Open(filepath); err != nil {
continue
}
if info, err := file.Stat(); err == nil && info.Size() > m.thresholdSize {
m.fire <- m.GenLogFileName(c)
}
file.Close()
}
}
}()
m.wg.Wait()
}
return m, nil
}
// Fire return the fire channel
func (m *manager) Fire() chan string {
return m.fire
}
// Close return stop the manager and return
func (m *manager) Close() {
close(m.context)
m.cr.Stop()
}
// ParseVolume parse the config volume format and return threshold
func (m *manager) ParseVolume(c *Config) {
s := []byte(strings.ToUpper(c.RollingVolumeSize))
if !(strings.Contains(string(s), "K") || strings.Contains(string(s), "KB") ||
strings.Contains(string(s), "M") || strings.Contains(string(s), "MB") ||
strings.Contains(string(s), "G") || strings.Contains(string(s), "GB") ||
strings.Contains(string(s), "T") || strings.Contains(string(s), "TB")) {
// set the default threshold with 1GB
m.thresholdSize = 1024 * 1024 * 1024
return
}
var unit int64 = 1
p, _ := strconv.Atoi(string(s[:len(s)-1]))
unitstr := string(s[len(s)-1])
if s[len(s)-1] == 'B' {
p, _ = strconv.Atoi(string(s[:len(s)-2]))
unitstr = string(s[len(s)-2:])
}
switch unitstr {
default:
fallthrough
case "T", "TB":
unit *= 1024
fallthrough
case "G", "GB":
unit *= 1024
fallthrough
case "M", "MB":
unit *= 1024
fallthrough
case "K", "KB":
unit *= 1024
}
m.thresholdSize = int64(p) * unit
}
// GenLogFileName generate the new log file name, filename should be absolute path
func (m *manager) GenLogFileName(c *Config) (filename string) {
m.lock.Lock()
// [path-to-log]/filename.log.2007010215041517
if c.Compress {
filename = path.Join(c.LogPath, c.FileName+".log.gz."+m.startAt.Format(c.TimeTagFormat))
} else {
filename = path.Join(c.LogPath, c.FileName+".log."+m.startAt.Format(c.TimeTagFormat))
}
// reset the start time to now
m.startAt = time.Now()
m.lock.Unlock()
return
}