-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathwriter.go
139 lines (119 loc) · 3.34 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
135
136
137
138
139
package zaphelper
import (
"io"
"os"
"path/filepath"
"sync"
"github.com/pkg/errors"
)
var (
// ensure we always implement io.WriteCloser
_ io.WriteCloser = (*Writer)(nil)
// osStat exists so it can be mocked out by tests.
osStat = os.Stat
)
// Writer is an io.WriteCloser that writes to the specified filename.
type Writer struct {
// Filename is the file to write logs to. Backup log files will be retained
// in the same directory. It uses <processname>-lumberjack.log in
// os.TempDir() if empty.
Filename string `json:"filename" yaml:"filename"`
file *os.File
mu sync.Mutex
}
// Write implements io.Writer.
func (w *Writer) Write(p []byte) (n int, err error) {
w.mu.Lock()
defer w.mu.Unlock()
if w.file == nil {
if err = w.openExistingOrNew(len(p)); err != nil {
return 0, err
}
}
n, err = w.file.Write(p)
return n, err
}
// Close implements io.Closer, and closes the current logfile.
func (w *Writer) Close() error {
w.mu.Lock()
defer w.mu.Unlock()
return w.close()
}
// close closes the file if it is open.
func (w *Writer) close() error {
if w.file == nil {
return nil
}
err := w.file.Close()
w.file = nil
return err
}
// Rotate causes Logger to close the existing log file and immediately create a
// new one. This is a helper function for applications that want to initiate
// rotations outside of the normal rotation rules, such as in response to
// SIGHUP.
func (w *Writer) Rotate() error {
w.mu.Lock()
defer w.mu.Unlock()
return w.rotate()
}
// rotate closes the current file, moves it aside with a timestamp in the name,
// (if it exists), opens a new file with the original filename, and then runs
// post-rotation processing and removal.
func (w *Writer) rotate() error {
if err := w.close(); err != nil {
return errors.Wrap(err, "close old file failed.")
}
if err := w.openNew(); err != nil {
return errors.Wrap(err, "open new file failed.")
}
return nil
}
// openNew opens a new log file for writing.
func (w *Writer) openNew() error {
err := os.MkdirAll(w.dir(), 0744)
if err != nil {
return errors.Wrap(err, "can't make directories for new logfile")
}
name := w.filename()
mode := os.FileMode(0644)
f, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY|os.O_APPEND, mode)
if err != nil {
return errors.Wrap(err, "can't open new logfile")
}
w.file = f
return nil
}
// openExistingOrNew opens the logfile if it exists and if the current write
// would not put it over MaxSize. If there is no such file or the write would
// put it over the MaxSize, a new file is created.
func (w *Writer) openExistingOrNew(writeLen int) error {
filename := w.filename()
_, err := osStat(filename)
if os.IsNotExist(err) {
return w.openNew()
}
if err != nil {
return errors.Wrap(err, "error getting log file info")
}
file, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
// if we fail to open the old log file for some reason, just ignore
// it and open a new log file.
return w.openNew()
}
w.file = file
return nil
}
// genFilename generates the name of the logfile from the current time.
func (w *Writer) filename() string {
if w.Filename != "" {
return w.Filename
}
name := filepath.Base(os.Args[0]) + "-zap.log"
return filepath.Join(os.TempDir(), name)
}
// dir returns the directory for the current filename.
func (w *Writer) dir() string {
return filepath.Dir(w.filename())
}