-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpatternfile.go
177 lines (159 loc) · 3.5 KB
/
patternfile.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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
package goolog2
import (
"fmt"
"os"
"strings"
"sync"
"sync/atomic"
"time"
)
const checkInterval int64 = 30
type patternFile struct {
pattern string
sync bool
currName string
currWriter FileWriter
lineMutex sync.Mutex
refcount int32
}
// Create new pattern file holder
//
// This implementation allows rotating of log files according
// specified pattern.
//
// The pattern is a string which can contain special sequences:
// %Y ..... year (4 digits)
// %m ..... month (01 - 12)
// %d ..... day (01 - 31)
// %H ..... hour (00 - 23)
// %M ..... minute (00 - 59)
// %% ..... %
//
// Parameters:
// timesrc: time source
// pattern: the filename pattern
// sync: flush the file after every line
// Returns:
// the new file holder
// Note: the reference counter is set to 1. You have to invoke Unref()
// to clean up the holder.
func NewPatternFile(
timesrc TimeSource,
pattern string,
sync bool,
) RotatableFileHolder {
holder := patternFile{
pattern: pattern,
sync: sync,
refcount: 1,
}
holder.Rotate(timesrc)
return &holder
}
func (this *patternFile) AccessWriter(
functor func(writer FileWriter),
) {
/* -- log the line */
this.lineMutex.Lock()
defer this.lineMutex.Unlock()
if this.currWriter != nil {
functor(this.currWriter)
if this.sync {
this.currWriter.Sync()
}
}
}
func (this *patternFile) Ref() FileHolder {
atomic.AddInt32(&this.refcount, 1)
return this
}
func (this *patternFile) Unref() {
refcount := atomic.AddInt32(&this.refcount, -1)
if refcount == 0 && this.currWriter != nil {
this.currWriter.Close()
this.currWriter = nil
}
}
// See LogRotator interface
func (this *patternFile) NeedRotate(
timesrc TimeSource,
) bool {
return true
}
// See LogRotator interface
func (this *patternFile) Rotate(
timesrc TimeSource,
) {
/* -- generate new filename */
newName := this.generateFilename(timesrc.Now())
/* -- if the filename differs switch the files */
if newName != this.currName {
/* -- No one other reads this value. So I can store it
without any synchronization */
this.currName = newName
/* -- open the new file */
newFile, err := os.OpenFile(
newName, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0644)
if err != nil {
newFile = nil
}
/* -- switch the file */
this.lineMutex.Lock()
oldWriter := this.currWriter
this.currWriter = newSimpleFileWriter(newFile, true)
this.lineMutex.Unlock()
/* -- close the old file */
if oldWriter != nil {
oldWriter.Close()
}
}
}
func (this *patternFile) generateFilename(
now time.Time,
) string {
type stateCode int
const (
INIT stateCode = iota
FMT
)
pattern := this.pattern
builder := &strings.Builder{}
state := INIT
for i := 0; i < len(pattern); i++ {
c := pattern[i]
switch state {
case INIT:
if c == '%' {
state = FMT
} else {
builder.WriteByte(c)
}
case FMT:
switch c {
case 'Y':
fmt.Fprintf(builder, "%04d", now.Year())
case 'm':
fmt.Fprintf(builder, "%02d", now.Month())
case 'd':
fmt.Fprintf(builder, "%02d", now.Day())
case 'H':
fmt.Fprintf(builder, "%02d", now.Hour())
case 'M':
fmt.Fprintf(builder, "%02d", now.Minute())
default:
builder.WriteByte(c)
}
state = INIT
}
}
return builder.String()
}
// See LogRotator interface
func (this *patternFile) GetNextCheckTime(
timesrc TimeSource,
) time.Time {
/* -- compute and set time of next check */
now := timesrc.Now()
nextCheck := now.Add(time.Duration(checkInterval) * time.Second)
return nextCheck
}