-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathwriter.go
134 lines (116 loc) · 3.59 KB
/
writer.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
// Package revolver provides a revolving file writer.
package revolver
import (
"fmt"
"io"
"os"
"path/filepath"
"sync"
)
type revWriter struct {
dir string
prefix string
suffix string
middle func() string
maxBytes int
maxFiles int
size int
file *os.File
lock *sync.Mutex // synchronizes file operations
}
// Must wraps the call to NewWriter and returns a io.WriteCloser or panics
func Must(w io.WriteCloser, err error) io.WriteCloser {
if err != nil {
panic(fmt.Errorf("could not create revolving log writer, %v", err))
}
return w
}
// New returns a io.WriteCloser that writes revolving files as specified by the given conf.
// Calling New will always create a new file even if there is space left in other files.
// If the configured directory doesn't exist it will be created.
func New(conf Conf) (io.WriteCloser, error) {
if err := ValidConf(conf); err != nil {
return nil, err
}
conf = clean(conf)
return NewQuick(conf.Dir, conf.Prefix, conf.Suffix, conf.Middle, conf.MaxBytes, conf.MaxFiles)
}
// NewQuick is like New with the difference that no Conf struct is needed.
// Calling New will always create a new file even if there is space left in other files.
// If the configured directory doesn't exist it will be created.
func NewQuick(dir, prefix, suffix string, middle func() string, maxBytes, maxFiles int) (io.WriteCloser, error) {
if prefix == "" {
return nil, fmt.Errorf("revolver, prefix can not be empty")
}
if middle == nil {
middle = func() string { return "" }
}
if maxBytes < 1 {
return nil, fmt.Errorf("revolver, maxBytes must be > 0")
}
if maxFiles < 1 {
return nil, fmt.Errorf("revolver, maxFiles must be > 0")
}
if err := setupDirs(dir); err != nil {
return nil, fmt.Errorf("revolver setup, %v", err)
}
if err := countAndRemoveFiles(dir, prefix, maxFiles); err != nil {
return nil, fmt.Errorf("revolver, remove, %v", err)
}
file, err := createFile(dir, prefix, suffix, middle)
if err != nil {
return nil, fmt.Errorf("revolver, create, %v", err)
}
return &revWriter{
dir: filepath.Clean(dir),
prefix: filepath.Clean(prefix),
suffix: suffix,
middle: middle,
maxBytes: maxBytes,
maxFiles: maxFiles,
file: file,
lock: &sync.Mutex{},
}, nil
}
// Write writes the given bytes into the current file. The specifics of the file are specified on writer creation.
// If there is not enough file space left,surplus files will be deleted and a new file will be created.
func (l *revWriter) Write(p []byte) (n int, err error) {
l.lock.Lock()
defer l.lock.Unlock()
size := len(p)
if size > l.maxBytes {
return 0, fmt.Errorf("revolver, bytes to write %d over max file size %d", size, l.maxBytes)
}
if l.file == nil || l.size+size > l.maxBytes {
if err := l.close(); err != nil {
return 0, fmt.Errorf("revolver, close, %v", err)
}
if err := countAndRemoveFiles(l.dir, l.prefix, l.maxFiles); err != nil {
return 0, fmt.Errorf("revolver, remove, %v", err)
}
file, err := createFile(l.dir, l.prefix, l.suffix, l.middle)
if err != nil {
return 0, fmt.Errorf("revolver, create, %v", err)
}
l.file = file
l.size = 0
}
l.size += size
return l.file.Write(p)
}
// Close closes the current log file and sets the writer reference to nil.
// If the file reference is nil, the returned err is always be nil.
// Writing to a nil referencing writer cleans up surplus files and creates a new file.
func (l *revWriter) Close() error {
l.lock.Lock()
defer l.lock.Unlock()
return l.close()
}
func (l *revWriter) close() error {
if l.file == nil {
return nil
}
err := l.file.Close()
l.file = nil
return err
}