deps(logging) vendor lumberjack (MIT)
log rotation License: MIT Signed-off-by: Brian Tiger Chow <brian@perfmode.com>
Brian Tiger Chow committed
Nov 16, 2014 at 03:24 UTC
2b72d9f14a8c0dc0279011ab484d1cd068999a0a
12 files changed
+1534
Godeps/Godeps.json
+5
@@ -147,6 +147,11 @@
147
{
148
"ImportPath": "github.com/tuxychandru/pubsub",
149
"Rev": "02de8aa2db3d570c5ab1be5ba67b456fd0fb7c4e"
150
+ },
151
+ {
152
+ "ImportPath": "gopkg.in/natefinch/lumberjack.v2",
153
+ "Comment": "v1.0-12-gd28785c",
154
+ "Rev": "d28785c2f27cd682d872df46ccd8232843629f54"
155
}
156
]
157
}
Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/.gitignore
new
+23
@@ -0,0 +1,23 @@
1
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
2
+*.o
3
+*.a
4
+*.so
5
+
6
+# Folders
7
+_obj
8
+_test
9
+
10
+# Architecture specific extensions/prefixes
11
+*.[568vq]
12
+[568vq].out
13
+
14
+*.cgo1.go
15
+*.cgo2.c
16
+_cgo_defun.c
17
+_cgo_gotypes.go
18
+_cgo_export.*
19
+
20
+_testmain.go
21
+
22
+*.exe
23
+*.test
Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/LICENSE
new
+21
@@ -0,0 +1,21 @@
1
+The MIT License (MIT)
2
+
3
+Copyright (c) 2014 Nate Finch
4
+
5
+Permission is hereby granted, free of charge, to any person obtaining a copy
6
+of this software and associated documentation files (the "Software"), to deal
7
+in the Software without restriction, including without limitation the rights
8
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+copies of the Software, and to permit persons to whom the Software is
10
+furnished to do so, subject to the following conditions:
11
+
12
+The above copyright notice and this permission notice shall be included in all
13
+copies or substantial portions of the Software.
14
+
15
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+SOFTWARE.
\ No newline at end of file
Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/README.md
new
+166
@@ -0,0 +1,166 @@
1
+# lumberjack [](https://godoc.org/gopkg.in/natefinch/lumberjack.v2) [](https://drone.io/github.com/natefinch/lumberjack/latest) [](https://ci.appveyor.com/project/natefinch/lumberjack)
2
+
3
+### Lumberjack is a Go package for writing logs to rolling files.
4
+
5
+Package lumberjack provides a rolling logger.
6
+
7
+Note that this is v2.0 of lumberjack, and should be imported using gopkg.in
8
+thusly:
9
+
10
+ import "gopkg.in/natefinch/lumberjack.v2"
11
+
12
+The package name remains simply lumberjack, and the code resides at
13
+https://github.com/natefinch/lumberjack under the v2.0 branch.
14
+
15
+Lumberjack is intended to be one part of a logging infrastructure.
16
+It is not an all-in-one solution, but instead is a pluggable
17
+component at the bottom of the logging stack that simply controls the files
18
+to which logs are written.
19
+
20
+Lumberjack plays well with any logging package that can write to an
21
+io.Writer, including the standard library's log package.
22
+
23
+Lumberjack assumes that only one process is writing to the output files.
24
+Using the same lumberjack configuration from multiple processes on the same
25
+machine will result in improper behavior.
26
+
27
+
28
+**Example**
29
+
30
+To use lumberjack with the standard library's log package, just pass it into the SetOutput function when your application starts.
31
+
32
+Code:
33
+
34
+```go
35
+log.SetOutput(&lumberjack.Logger{
36
+ Filename: "/var/log/myapp/foo.log",
37
+ MaxSize: 500, // megabytes
38
+ MaxBackups: 3,
39
+ MaxAge: 28, //days
40
+})
41
+```
42
+
43
+
44
+
45
+## type Logger
46
+``` go
47
+type Logger struct {
48
+ // Filename is the file to write logs to. Backup log files will be retained
49
+ // in the same directory. It uses <processname>-lumberjack.log in
50
+ // os.TempDir() if empty.
51
+ Filename string `json:"filename" yaml:"filename"`
52
+
53
+ // MaxSize is the maximum size in megabytes of the log file before it gets
54
+ // rotated. It defaults to 100 megabytes.
55
+ MaxSize int `json:"maxsize" yaml:"maxsize"`
56
+
57
+ // MaxAge is the maximum number of days to retain old log files based on the
58
+ // timestamp encoded in their filename. Note that a day is defined as 24
59
+ // hours and may not exactly correspond to calendar days due to daylight
60
+ // savings, leap seconds, etc. The default is not to remove old log files
61
+ // based on age.
62
+ MaxAge int `json:"maxage" yaml:"maxage"`
63
+
64
+ // MaxBackups is the maximum number of old log files to retain. The default
65
+ // is to retain all old log files (though MaxAge may still cause them to get
66
+ // deleted.)
67
+ MaxBackups int `json:"maxbackups" yaml:"maxbackups"`
68
+
69
+ // LocalTime determines if the time used for formatting the timestamps in
70
+ // backup files is the computer's local time. The default is to use UTC
71
+ // time.
72
+ LocalTime bool `json:"localtime" yaml:"localtime"`
73
+ // contains filtered or unexported fields
74
+}
75
+```
76
+Logger is an io.WriteCloser that writes to the specified filename.
77
+
78
+Logger opens or creates the logfile on first Write. If the file exists and
79
+is less than MaxSize megabytes, lumberjack will open and append to that file.
80
+If the file exists and its size is >= MaxSize megabytes, the file is renamed
81
+by putting the current time in a timestamp in the name immediately before the
82
+file's extension (or the end of the filename if there's no extension). A new
83
+log file is then created using original filename.
84
+
85
+Whenever a write would cause the current log file exceed MaxSize megabytes,
86
+the current file is closed, renamed, and a new log file created with the
87
+original name. Thus, the filename you give Logger is always the "current" log
88
+file.
89
+
90
+### Cleaning Up Old Log Files
91
+Whenever a new logfile gets created, old log files may be deleted. The most
92
+recent files according to the encoded timestamp will be retained, up to a
93
+number equal to MaxBackups (or all of them if MaxBackups is 0). Any files
94
+with an encoded timestamp older than MaxAge days are deleted, regardless of
95
+MaxBackups. Note that the time encoded in the timestamp is the rotation
96
+time, which may differ from the last time that file was written to.
97
+
98
+If MaxBackups and MaxAge are both 0, no old log files will be deleted.
99
+
100
+
101
+
102
+
103
+
104
+
105
+
106
+
107
+
108
+
109
+
110
+### func (\*Logger) Close
111
+``` go
112
+func (l *Logger) Close() error
113
+```
114
+Close implements io.Closer, and closes the current logfile.
115
+
116
+
117
+
118
+### func (\*Logger) Rotate
119
+``` go
120
+func (l *Logger) Rotate() error
121
+```
122
+Rotate causes Logger to close the existing log file and immediately create a
123
+new one. This is a helper function for applications that want to initiate
124
+rotations outside of the normal rotation rules, such as in response to
125
+SIGHUP. After rotating, this initiates a cleanup of old log files according
126
+to the normal rules.
127
+
128
+**Example**
129
+
130
+Example of how to rotate in response to SIGHUP.
131
+
132
+Code:
133
+
134
+```go
135
+l := &lumberjack.Logger{}
136
+log.SetOutput(l)
137
+c := make(chan os.Signal, 1)
138
+signal.Notify(c, syscall.SIGHUP)
139
+
140
+go func() {
141
+ for {
142
+ <-c
143
+ l.Rotate()
144
+ }
145
+}()
146
+```
147
+
148
+### func (\*Logger) Write
149
+``` go
150
+func (l *Logger) Write(p []byte) (n int, err error)
151
+```
152
+Write implements io.Writer. If a write would cause the log file to be larger
153
+than MaxSize, the file is closed, renamed to include a timestamp of the
154
+current time, and a new log file is created using the original log file name.
155
+If the length of the write is greater than MaxSize, an error is returned.
156
+
157
+
158
+
159
+
160
+
161
+
162
+
163
+
164
+
165
+- - -
166
+Generated by [godoc2md](http://godoc.org/github.com/davecheney/godoc2md)
Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/chown.go
new
+11
@@ -0,0 +1,11 @@
1
+// +build !linux
2
+
3
+package lumberjack
4
+
5
+import (
6
+ "os"
7
+)
8
+
9
+func chown(_ string, _ os.FileInfo) error {
10
+ return nil
11
+}
Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/chown_linux.go
new
+19
@@ -0,0 +1,19 @@
1
+package lumberjack
2
+
3
+import (
4
+ "os"
5
+ "syscall"
6
+)
7
+
8
+// os_Chown is a var so we can mock it out during tests.
9
+var os_Chown = os.Chown
10
+
11
+func chown(name string, info os.FileInfo) error {
12
+ f, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, info.Mode())
13
+ if err != nil {
14
+ return err
15
+ }
16
+ f.Close()
17
+ stat := info.Sys().(*syscall.Stat_t)
18
+ return os_Chown(name, int(stat.Uid), int(stat.Gid))
19
+}
Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/example_test.go
new
+18
@@ -0,0 +1,18 @@
1
+package lumberjack_test
2
+
3
+import (
4
+ "log"
5
+
6
+ "github.com/natefinch/lumberjack"
7
+)
8
+
9
+// To use lumberjack with the standard library's log package, just pass it into
10
+// the SetOutput function when your application starts.
11
+func Example() {
12
+ log.SetOutput(&lumberjack.Logger{
13
+ Filename: "/var/log/myapp/foo.log",
14
+ MaxSize: 500, // megabytes
15
+ MaxBackups: 3,
16
+ MaxAge: 28, // days
17
+ })
18
+}
Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/linux_test.go
new
+104
@@ -0,0 +1,104 @@
1
+// +build linux
2
+
3
+package lumberjack
4
+
5
+import (
6
+ "os"
7
+ "syscall"
8
+ "testing"
9
+)
10
+
11
+func TestMaintainMode(t *testing.T) {
12
+ currentTime = fakeTime
13
+ dir := makeTempDir("TestMaintainMode", t)
14
+ defer os.RemoveAll(dir)
15
+
16
+ filename := logFile(dir)
17
+
18
+ mode := os.FileMode(0770)
19
+ f, err := os.OpenFile(filename, os.O_CREATE|os.O_RDWR, mode)
20
+ isNil(err, t)
21
+ f.Close()
22
+
23
+ l := &Logger{
24
+ Filename: filename,
25
+ MaxBackups: 1,
26
+ MaxSize: 100, // megabytes
27
+ }
28
+ defer l.Close()
29
+ b := []byte("boo!")
30
+ n, err := l.Write(b)
31
+ isNil(err, t)
32
+ equals(len(b), n, t)
33
+
34
+ newFakeTime()
35
+
36
+ err = l.Rotate()
37
+ isNil(err, t)
38
+
39
+ filename2 := backupFile(dir)
40
+ info, err := os.Stat(filename)
41
+ isNil(err, t)
42
+ info2, err := os.Stat(filename2)
43
+ isNil(err, t)
44
+ equals(mode, info.Mode(), t)
45
+ equals(mode, info2.Mode(), t)
46
+}
47
+
48
+func TestMaintainOwner(t *testing.T) {
49
+ fakeC := fakeChown{}
50
+ os_Chown = fakeC.Set
51
+ os_Stat = fakeStat
52
+ defer func() {
53
+ os_Chown = os.Chown
54
+ os_Stat = os.Stat
55
+ }()
56
+ currentTime = fakeTime
57
+ dir := makeTempDir("TestMaintainOwner", t)
58
+ defer os.RemoveAll(dir)
59
+
60
+ filename := logFile(dir)
61
+
62
+ l := &Logger{
63
+ Filename: filename,
64
+ MaxBackups: 1,
65
+ MaxSize: 100, // megabytes
66
+ }
67
+ defer l.Close()
68
+ b := []byte("boo!")
69
+ n, err := l.Write(b)
70
+ isNil(err, t)
71
+ equals(len(b), n, t)
72
+
73
+ newFakeTime()
74
+
75
+ err = l.Rotate()
76
+ isNil(err, t)
77
+
78
+ equals(555, fakeC.uid, t)
79
+ equals(666, fakeC.gid, t)
80
+}
81
+
82
+type fakeChown struct {
83
+ name string
84
+ uid int
85
+ gid int
86
+}
87
+
88
+func (f *fakeChown) Set(name string, uid, gid int) error {
89
+ f.name = name
90
+ f.uid = uid
91
+ f.gid = gid
92
+ return nil
93
+}
94
+
95
+func fakeStat(name string) (os.FileInfo, error) {
96
+ info, err := os.Stat(name)
97
+ if err != nil {
98
+ return info, err
99
+ }
100
+ stat := info.Sys().(*syscall.Stat_t)
101
+ stat.Uid = 555
102
+ stat.Gid = 666
103
+ return info, nil
104
+}
Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/lumberjack.go
new
+415
@@ -0,0 +1,415 @@
1
+// Package lumberjack provides a rolling logger.
2
+//
3
+// Note that this is v2.0 of lumberjack, and should be imported using gopkg.in
4
+// thusly:
5
+//
6
+// import "gopkg.in/natefinch/lumberjack.v2"
7
+//
8
+// The package name remains simply lumberjack, and the code resides at
9
+// https://github.com/natefinch/lumberjack under the v2.0 branch.
10
+//
11
+// Lumberjack is intended to be one part of a logging infrastructure.
12
+// It is not an all-in-one solution, but instead is a pluggable
13
+// component at the bottom of the logging stack that simply controls the files
14
+// to which logs are written.
15
+//
16
+// Lumberjack plays well with any logging package that can write to an
17
+// io.Writer, including the standard library's log package.
18
+//
19
+// Lumberjack assumes that only one process is writing to the output files.
20
+// Using the same lumberjack configuration from multiple processes on the same
21
+// machine will result in improper behavior.
22
+package lumberjack
23
+
24
+import (
25
+ "fmt"
26
+ "io"
27
+ "io/ioutil"
28
+ "os"
29
+ "path/filepath"
30
+ "sort"
31
+ "strings"
32
+ "sync"
33
+ "time"
34
+)
35
+
36
+const (
37
+ backupTimeFormat = "2006-01-02T15-04-05.000"
38
+ defaultMaxSize = 100
39
+)
40
+
41
+// ensure we always implement io.WriteCloser
42
+var _ io.WriteCloser = (*Logger)(nil)
43
+
44
+// Logger is an io.WriteCloser that writes to the specified filename.
45
+//
46
+// Logger opens or creates the logfile on first Write. If the file exists and
47
+// is less than MaxSize megabytes, lumberjack will open and append to that file.
48
+// If the file exists and its size is >= MaxSize megabytes, the file is renamed
49
+// by putting the current time in a timestamp in the name immediately before the
50
+// file's extension (or the end of the filename if there's no extension). A new
51
+// log file is then created using original filename.
52
+//
53
+// Whenever a write would cause the current log file exceed MaxSize megabytes,
54
+// the current file is closed, renamed, and a new log file created with the
55
+// original name. Thus, the filename you give Logger is always the "current" log
56
+// file.
57
+//
58
+// Cleaning Up Old Log Files
59
+//
60
+// Whenever a new logfile gets created, old log files may be deleted. The most
61
+// recent files according to the encoded timestamp will be retained, up to a
62
+// number equal to MaxBackups (or all of them if MaxBackups is 0). Any files
63
+// with an encoded timestamp older than MaxAge days are deleted, regardless of
64
+// MaxBackups. Note that the time encoded in the timestamp is the rotation
65
+// time, which may differ from the last time that file was written to.
66
+//
67
+// If MaxBackups and MaxAge are both 0, no old log files will be deleted.
68
+type Logger struct {
69
+ // Filename is the file to write logs to. Backup log files will be retained
70
+ // in the same directory. It uses <processname>-lumberjack.log in
71
+ // os.TempDir() if empty.
72
+ Filename string `json:"filename" yaml:"filename"`
73
+
74
+ // MaxSize is the maximum size in megabytes of the log file before it gets
75
+ // rotated. It defaults to 100 megabytes.
76
+ MaxSize int `json:"maxsize" yaml:"maxsize"`
77
+
78
+ // MaxAge is the maximum number of days to retain old log files based on the
79
+ // timestamp encoded in their filename. Note that a day is defined as 24
80
+ // hours and may not exactly correspond to calendar days due to daylight
81
+ // savings, leap seconds, etc. The default is not to remove old log files
82
+ // based on age.
83
+ MaxAge int `json:"maxage" yaml:"maxage"`
84
+
85
+ // MaxBackups is the maximum number of old log files to retain. The default
86
+ // is to retain all old log files (though MaxAge may still cause them to get
87
+ // deleted.)
88
+ MaxBackups int `json:"maxbackups" yaml:"maxbackups"`
89
+
90
+ // LocalTime determines if the time used for formatting the timestamps in
91
+ // backup files is the computer's local time. The default is to use UTC
92
+ // time.
93
+ LocalTime bool `json:"localtime" yaml:"localtime"`
94
+
95
+ size int64
96
+ file *os.File
97
+ mu sync.Mutex
98
+}
99
+
100
+var (
101
+ // currentTime exists so it can be mocked out by tests.
102
+ currentTime = time.Now
103
+
104
+ // os_Stat exists so it can be mocked out by tests.
105
+ os_Stat = os.Stat
106
+
107
+ // megabyte is the conversion factor between MaxSize and bytes. It is a
108
+ // variable so tests can mock it out and not need to write megabytes of data
109
+ // to disk.
110
+ megabyte = 1024 * 1024
111
+)
112
+
113
+// Write implements io.Writer. If a write would cause the log file to be larger
114
+// than MaxSize, the file is closed, renamed to include a timestamp of the
115
+// current time, and a new log file is created using the original log file name.
116
+// If the length of the write is greater than MaxSize, an error is returned.
117
+func (l *Logger) Write(p []byte) (n int, err error) {
118
+ l.mu.Lock()
119
+ defer l.mu.Unlock()
120
+
121
+ writeLen := int64(len(p))
122
+ if writeLen > l.max() {
123
+ return 0, fmt.Errorf(
124
+ "write length %d exceeds maximum file size %d", writeLen, l.max(),
125
+ )
126
+ }
127
+
128
+ if l.file == nil {
129
+ if err = l.openExistingOrNew(len(p)); err != nil {
130
+ return 0, err
131
+ }
132
+ }
133
+
134
+ if l.size+writeLen > l.max() {
135
+ if err := l.rotate(); err != nil {
136
+ return 0, err
137
+ }
138
+ }
139
+
140
+ n, err = l.file.Write(p)
141
+ l.size += int64(n)
142
+
143
+ return n, err
144
+}
145
+
146
+// Close implements io.Closer, and closes the current logfile.
147
+func (l *Logger) Close() error {
148
+ l.mu.Lock()
149
+ defer l.mu.Unlock()
150
+ return l.close()
151
+}
152
+
153
+// close closes the file if it is open.
154
+func (l *Logger) close() error {
155
+ if l.file == nil {
156
+ return nil
157
+ }
158
+ err := l.file.Close()
159
+ l.file = nil
160
+ return err
161
+}
162
+
163
+// Rotate causes Logger to close the existing log file and immediately create a
164
+// new one. This is a helper function for applications that want to initiate
165
+// rotations outside of the normal rotation rules, such as in response to
166
+// SIGHUP. After rotating, this initiates a cleanup of old log files according
167
+// to the normal rules.
168
+func (l *Logger) Rotate() error {
169
+ l.mu.Lock()
170
+ defer l.mu.Unlock()
171
+ return l.rotate()
172
+}
173
+
174
+// rotate closes the current file, moves it aside with a timestamp in the name,
175
+// (if it exists), opens a new file with the original filename, and then runs
176
+// cleanup.
177
+func (l *Logger) rotate() error {
178
+ if err := l.close(); err != nil {
179
+ return err
180
+ }
181
+
182
+ if err := l.openNew(); err != nil {
183
+ return err
184
+ }
185
+ return l.cleanup()
186
+}
187
+
188
+// openNew opens a new log file for writing, moving any old log file out of the
189
+// way. This methods assumes the file has already been closed.
190
+func (l *Logger) openNew() error {
191
+ err := os.MkdirAll(l.dir(), 0744)
192
+ if err != nil {
193
+ return fmt.Errorf("can't make directories for new logfile: %s", err)
194
+ }
195
+
196
+ name := l.filename()
197
+ mode := os.FileMode(0644)
198
+ info, err := os_Stat(name)
199
+ if err == nil {
200
+ // Copy the mode off the old logfile.
201
+ mode = info.Mode()
202
+ // move the existing file
203
+ newname := backupName(name, l.LocalTime)
204
+ if err := os.Rename(name, newname); err != nil {
205
+ return fmt.Errorf("can't rename log file: %s", err)
206
+ }
207
+
208
+ // this is a no-op anywhere but linux
209
+ if err := chown(name, info); err != nil {
210
+ return err
211
+ }
212
+ }
213
+
214
+ // we use truncate here because this should only get called when we've moved
215
+ // the file ourselves. if someone else creates the file in the meantime,
216
+ // just wipe out the contents.
217
+ f, err := os.OpenFile(name, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, mode)
218
+ if err != nil {
219
+ return fmt.Errorf("can't open new logfile: %s", err)
220
+ }
221
+ l.file = f
222
+ l.size = 0
223
+ return nil
224
+}
225
+
226
+// backupName creates a new filename from the given name, inserting a timestamp
227
+// between the filename and the extension, using the local time if requested
228
+// (otherwise UTC).
229
+func backupName(name string, local bool) string {
230
+ dir := filepath.Dir(name)
231
+ filename := filepath.Base(name)
232
+ ext := filepath.Ext(filename)
233
+ prefix := filename[:len(filename)-len(ext)]
234
+ t := currentTime()
235
+ if !local {
236
+ t = t.UTC()
237
+ }
238
+
239
+ timestamp := t.Format(backupTimeFormat)
240
+ return filepath.Join(dir, fmt.Sprintf("%s-%s%s", prefix, timestamp, ext))
241
+}
242
+
243
+// openExistingOrNew opens the logfile if it exists and if the current write
244
+// would not put it over MaxSize. If there is no such file or the write would
245
+// put it over the MaxSize, a new file is created.
246
+func (l *Logger) openExistingOrNew(writeLen int) error {
247
+ filename := l.filename()
248
+ info, err := os_Stat(filename)
249
+ if os.IsNotExist(err) {
250
+ return l.openNew()
251
+ }
252
+ if err != nil {
253
+ return fmt.Errorf("error getting log file info: %s", err)
254
+ }
255
+ // the first file we find that matches our pattern will be the most
256
+ // recently modified log file.
257
+ if info.Size()+int64(writeLen) < l.max() {
258
+ file, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, 0644)
259
+ if err == nil {
260
+ l.file = file
261
+ l.size = info.Size()
262
+ return nil
263
+ }
264
+ // if we fail to open the old log file for some reason, just ignore
265
+ // it and open a new log file.
266
+ }
267
+ return l.openNew()
268
+}
269
+
270
+// genFilename generates the name of the logfile from the current time.
271
+func (l *Logger) filename() string {
272
+ if l.Filename != "" {
273
+ return l.Filename
274
+ }
275
+ name := filepath.Base(os.Args[0]) + "-lumberjack.log"
276
+ return filepath.Join(os.TempDir(), name)
277
+}
278
+
279
+// cleanup deletes old log files, keeping at most l.MaxBackups files, as long as
280
+// none of them are older than MaxAge.
281
+func (l *Logger) cleanup() error {
282
+ if l.MaxBackups == 0 && l.MaxAge == 0 {
283
+ return nil
284
+ }
285
+
286
+ files, err := l.oldLogFiles()
287
+ if err != nil {
288
+ return err
289
+ }
290
+
291
+ var deletes []logInfo
292
+
293
+ if l.MaxBackups > 0 && l.MaxBackups < len(files) {
294
+ deletes = files[l.MaxBackups:]
295
+ files = files[:l.MaxBackups]
296
+ }
297
+ if l.MaxAge > 0 {
298
+ diff := time.Duration(int64(24*time.Hour) * int64(l.MaxAge))
299
+
300
+ cutoff := currentTime().Add(-1 * diff)
301
+
302
+ for _, f := range files {
303
+ if f.timestamp.Before(cutoff) {
304
+ deletes = append(deletes, f)
305
+ }
306
+ }
307
+ }
308
+
309
+ if len(deletes) == 0 {
310
+ return nil
311
+ }
312
+
313
+ go deleteAll(l.dir(), deletes)
314
+
315
+ return nil
316
+}
317
+
318
+func deleteAll(dir string, files []logInfo) {
319
+ // remove files on a separate goroutine
320
+ for _, f := range files {
321
+ // what am I going to do, log this?
322
+ _ = os.Remove(filepath.Join(dir, f.Name()))
323
+ }
324
+}
325
+
326
+// oldLogFiles returns the list of backup log files stored in the same
327
+// directory as the current log file, sorted by ModTime
328
+func (l *Logger) oldLogFiles() ([]logInfo, error) {
329
+ files, err := ioutil.ReadDir(l.dir())
330
+ if err != nil {
331
+ return nil, fmt.Errorf("can't read log file directory: %s", err)
332
+ }
333
+ logFiles := []logInfo{}
334
+
335
+ prefix, ext := l.prefixAndExt()
336
+
337
+ for _, f := range files {
338
+ if f.IsDir() {
339
+ continue
340
+ }
341
+
342
+ name := l.timeFromName(f.Name(), prefix, ext)
343
+ if name == "" {
344
+ continue
345
+ }
346
+ t, err := time.Parse(backupTimeFormat, name)
347
+ if err == nil {
348
+ logFiles = append(logFiles, logInfo{t, f})
349
+ }
350
+ }
351
+
352
+ sort.Sort(byFormatTime(logFiles))
353
+
354
+ return logFiles, nil
355
+}
356
+
357
+// timeFromName extracts the formatted time from the filename by stripping off
358
+// the filename's prefix and extension. This prevents someone's filename from
359
+// confusing time.parse.
360
+func (l *Logger) timeFromName(filename, prefix, ext string) string {
361
+ if !strings.HasPrefix(filename, prefix) {
362
+ return ""
363
+ }
364
+ filename = filename[len(prefix):]
365
+
366
+ if !strings.HasSuffix(filename, ext) {
367
+ return ""
368
+ }
369
+ filename = filename[:len(filename)-len(ext)]
370
+ return filename
371
+}
372
+
373
+// max returns the maximum size in bytes of log files before rolling.
374
+func (l *Logger) max() int64 {
375
+ if l.MaxSize == 0 {
376
+ return int64(defaultMaxSize * megabyte)
377
+ }
378
+ return int64(l.MaxSize) * int64(megabyte)
379
+}
380
+
381
+// dir returns the directory for the current filename.
382
+func (l *Logger) dir() string {
383
+ return filepath.Dir(l.filename())
384
+}
385
+
386
+// prefixAndExt returns the filename part and extension part from the Logger's
387
+// filename.
388
+func (l *Logger) prefixAndExt() (prefix, ext string) {
389
+ filename := filepath.Base(l.filename())
390
+ ext = filepath.Ext(filename)
391
+ prefix = filename[:len(filename)-len(ext)] + "-"
392
+ return prefix, ext
393
+}
394
+
395
+// logInfo is a convenience struct to return the filename and its embedded
396
+// timestamp.
397
+type logInfo struct {
398
+ timestamp time.Time
399
+ os.FileInfo
400
+}
401
+
402
+// byFormatTime sorts by newest time formatted in the name.
403
+type byFormatTime []logInfo
404
+
405
+func (b byFormatTime) Less(i, j int) bool {
406
+ return b[i].timestamp.After(b[j].timestamp)
407
+}
408
+
409
+func (b byFormatTime) Swap(i, j int) {
410
+ b[i], b[j] = b[j], b[i]
411
+}
412
+
413
+func (b byFormatTime) Len() int {
414
+ return len(b)
415
+}
Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/lumberjack_test.go
new
+634
@@ -0,0 +1,634 @@
1
+package lumberjack
2
+
3
+import (
4
+ "encoding/json"
5
+ "fmt"
6
+ "io/ioutil"
7
+ "os"
8
+ "path/filepath"
9
+ "testing"
10
+ "time"
11
+
12
+ "github.com/BurntSushi/toml"
13
+ "gopkg.in/yaml.v1"
14
+)
15
+
16
+// !!!NOTE!!!
17
+//
18
+// Running these tests in parallel will almost certainly cause sporadic (or even
19
+// regular) failures, because they're all messing with the same global variable
20
+// that controls the logic's mocked time.Now. So... don't do that.
21
+
22
+// Since all the tests uses the time to determine filenames etc, we need to
23
+// control the wall clock as much as possible, which means having a wall clock
24
+// that doesn't change unless we want it to.
25
+var fakeCurrentTime = time.Now()
26
+
27
+func fakeTime() time.Time {
28
+ return fakeCurrentTime
29
+}
30
+
31
+func TestNewFile(t *testing.T) {
32
+ currentTime = fakeTime
33
+
34
+ dir := makeTempDir("TestNewFile", t)
35
+ defer os.RemoveAll(dir)
36
+ l := &Logger{
37
+ Filename: logFile(dir),
38
+ }
39
+ defer l.Close()
40
+ b := []byte("boo!")
41
+ n, err := l.Write(b)
42
+ isNil(err, t)
43
+ equals(len(b), n, t)
44
+ existsWithLen(logFile(dir), n, t)
45
+ fileCount(dir, 1, t)
46
+}
47
+
48
+func TestOpenExisting(t *testing.T) {
49
+ currentTime = fakeTime
50
+ dir := makeTempDir("TestOpenExisting", t)
51
+ defer os.RemoveAll(dir)
52
+
53
+ filename := logFile(dir)
54
+ data := []byte("foo!")
55
+ err := ioutil.WriteFile(filename, data, 0644)
56
+ isNil(err, t)
57
+ existsWithLen(filename, len(data), t)
58
+
59
+ l := &Logger{
60
+ Filename: filename,
61
+ }
62
+ defer l.Close()
63
+ b := []byte("boo!")
64
+ n, err := l.Write(b)
65
+ isNil(err, t)
66
+ equals(len(b), n, t)
67
+
68
+ // make sure the file got appended
69
+ existsWithLen(filename, len(data)+n, t)
70
+
71
+ // make sure no other files were created
72
+ fileCount(dir, 1, t)
73
+}
74
+
75
+func TestWriteTooLong(t *testing.T) {
76
+ currentTime = fakeTime
77
+ megabyte = 1
78
+ dir := makeTempDir("TestWriteTooLong", t)
79
+ defer os.RemoveAll(dir)
80
+ l := &Logger{
81
+ Filename: logFile(dir),
82
+ MaxSize: 5,
83
+ }
84
+ defer l.Close()
85
+ b := []byte("booooooooooooooo!")
86
+ n, err := l.Write(b)
87
+ notNil(err, t)
88
+ equals(0, n, t)
89
+ equals(err.Error(),
90
+ fmt.Sprintf("write length %d exceeds maximum file size %d", len(b), l.MaxSize), t)
91
+ _, err = os.Stat(logFile(dir))
92
+ assert(os.IsNotExist(err), t, "File exists, but should not have been created")
93
+}
94
+
95
+func TestMakeLogDir(t *testing.T) {
96
+ currentTime = fakeTime
97
+ dir := time.Now().Format("TestMakeLogDir" + backupTimeFormat)
98
+ dir = filepath.Join(os.TempDir(), dir)
99
+ defer os.RemoveAll(dir)
100
+ filename := logFile(dir)
101
+ l := &Logger{
102
+ Filename: filename,
103
+ }
104
+ defer l.Close()
105
+ b := []byte("boo!")
106
+ n, err := l.Write(b)
107
+ isNil(err, t)
108
+ equals(len(b), n, t)
109
+ existsWithLen(logFile(dir), n, t)
110
+ fileCount(dir, 1, t)
111
+}
112
+
113
+func TestDefaultFilename(t *testing.T) {
114
+ currentTime = fakeTime
115
+ dir := os.TempDir()
116
+ filename := filepath.Join(dir, filepath.Base(os.Args[0])+"-lumberjack.log")
117
+ defer os.Remove(filename)
118
+ l := &Logger{}
119
+ defer l.Close()
120
+ b := []byte("boo!")
121
+ n, err := l.Write(b)
122
+
123
+ isNil(err, t)
124
+ equals(len(b), n, t)
125
+ existsWithLen(filename, n, t)
126
+}
127
+
128
+func TestAutoRotate(t *testing.T) {
129
+ currentTime = fakeTime
130
+ megabyte = 1
131
+
132
+ dir := makeTempDir("TestAutoRotate", t)
133
+ defer os.RemoveAll(dir)
134
+
135
+ filename := logFile(dir)
136
+ l := &Logger{
137
+ Filename: filename,
138
+ MaxSize: 10,
139
+ }
140
+ defer l.Close()
141
+ b := []byte("boo!")
142
+ n, err := l.Write(b)
143
+ isNil(err, t)
144
+ equals(len(b), n, t)
145
+
146
+ existsWithLen(filename, n, t)
147
+ fileCount(dir, 1, t)
148
+
149
+ newFakeTime()
150
+
151
+ b2 := []byte("foooooo!")
152
+ n, err = l.Write(b2)
153
+ isNil(err, t)
154
+ equals(len(b2), n, t)
155
+
156
+ // the old logfile should be moved aside and the main logfile should have
157
+ // only the last write in it.
158
+ existsWithLen(filename, n, t)
159
+
160
+ // the backup file will use the current fake time and have the old contents.
161
+ existsWithLen(backupFile(dir), len(b), t)
162
+
163
+ fileCount(dir, 2, t)
164
+}
165
+
166
+func TestFirstWriteRotate(t *testing.T) {
167
+ currentTime = fakeTime
168
+ megabyte = 1
169
+ dir := makeTempDir("TestFirstWriteRotate", t)
170
+ defer os.RemoveAll(dir)
171
+
172
+ filename := logFile(dir)
173
+ l := &Logger{
174
+ Filename: filename,
175
+ MaxSize: 10,
176
+ }
177
+ defer l.Close()
178
+
179
+ start := []byte("boooooo!")
180
+ err := ioutil.WriteFile(filename, start, 0600)
181
+ isNil(err, t)
182
+
183
+ newFakeTime()
184
+
185
+ // this would make us rotate
186
+ b := []byte("fooo!")
187
+ n, err := l.Write(b)
188
+ isNil(err, t)
189
+ equals(len(b), n, t)
190
+
191
+ existsWithLen(filename, n, t)
192
+ existsWithLen(backupFile(dir), len(start), t)
193
+
194
+ fileCount(dir, 2, t)
195
+}
196
+
197
+func TestMaxBackups(t *testing.T) {
198
+ currentTime = fakeTime
199
+ megabyte = 1
200
+ dir := makeTempDir("TestMaxBackups", t)
201
+ defer os.RemoveAll(dir)
202
+
203
+ filename := logFile(dir)
204
+ l := &Logger{
205
+ Filename: filename,
206
+ MaxSize: 10,
207
+ MaxBackups: 1,
208
+ }
209
+ defer l.Close()
210
+ b := []byte("boo!")
211
+ n, err := l.Write(b)
212
+ isNil(err, t)
213
+ equals(len(b), n, t)
214
+
215
+ existsWithLen(filename, n, t)
216
+ fileCount(dir, 1, t)
217
+
218
+ newFakeTime()
219
+
220
+ // this will put us over the max
221
+ b2 := []byte("foooooo!")
222
+ n, err = l.Write(b2)
223
+ isNil(err, t)
224
+ equals(len(b2), n, t)
225
+
226
+ // this will use the new fake time
227
+ secondFilename := backupFile(dir)
228
+ existsWithLen(secondFilename, len(b), t)
229
+
230
+ // make sure the old file still exists with the same size.
231
+ existsWithLen(filename, n, t)
232
+
233
+ fileCount(dir, 2, t)
234
+
235
+ newFakeTime()
236
+
237
+ // this will make us rotate again
238
+ n, err = l.Write(b2)
239
+ isNil(err, t)
240
+ equals(len(b2), n, t)
241
+
242
+ // this will use the new fake time
243
+ thirdFilename := backupFile(dir)
244
+ existsWithLen(thirdFilename, len(b2), t)
245
+
246
+ existsWithLen(filename, n, t)
247
+
248
+ // we need to wait a little bit since the files get deleted on a different
249
+ // goroutine.
250
+ <-time.After(time.Millisecond * 10)
251
+
252
+ // should only have two files in the dir still
253
+ fileCount(dir, 2, t)
254
+
255
+ // second file name should still exist
256
+ existsWithLen(thirdFilename, len(b2), t)
257
+
258
+ // should have deleted the first backup
259
+ notExist(secondFilename, t)
260
+
261
+ // now test that we don't delete directories or non-logfile files
262
+
263
+ newFakeTime()
264
+
265
+ // create a file that is close to but different from the logfile name.
266
+ // It shouldn't get caught by our deletion filters.
267
+ notlogfile := logFile(dir) + ".foo"
268
+ err = ioutil.WriteFile(notlogfile, []byte("data"), 0644)
269
+ isNil(err, t)
270
+
271
+ // Make a directory that exactly matches our log file filters... it still
272
+ // shouldn't get caught by the deletion filter since it's a directory.
273
+ notlogfiledir := backupFile(dir)
274
+ err = os.Mkdir(notlogfiledir, 0700)
275
+ isNil(err, t)
276
+
277
+ newFakeTime()
278
+
279
+ // this will make us rotate again
280
+ n, err = l.Write(b2)
281
+ isNil(err, t)
282
+ equals(len(b2), n, t)
283
+
284
+ // this will use the new fake time
285
+ fourthFilename := backupFile(dir)
286
+ existsWithLen(fourthFilename, len(b2), t)
287
+
288
+ // we need to wait a little bit since the files get deleted on a different
289
+ // goroutine.
290
+ <-time.After(time.Millisecond * 10)
291
+
292
+ // We should have four things in the directory now - the 2 log files, the
293
+ // not log file, and the directory
294
+ fileCount(dir, 4, t)
295
+
296
+ // third file name should still exist
297
+ existsWithLen(filename, n, t)
298
+
299
+ existsWithLen(fourthFilename, len(b2), t)
300
+
301
+ // should have deleted the first filename
302
+ notExist(thirdFilename, t)
303
+
304
+ // the not-a-logfile should still exist
305
+ exists(notlogfile, t)
306
+
307
+ // the directory
308
+ exists(notlogfiledir, t)
309
+}
310
+
311
+func TestMaxAge(t *testing.T) {
312
+ currentTime = fakeTime
313
+ megabyte = 1
314
+
315
+ dir := makeTempDir("TestMaxAge", t)
316
+ defer os.RemoveAll(dir)
317
+
318
+ filename := logFile(dir)
319
+ l := &Logger{
320
+ Filename: filename,
321
+ MaxSize: 10,
322
+ MaxAge: 1,
323
+ }
324
+ defer l.Close()
325
+ b := []byte("boo!")
326
+ n, err := l.Write(b)
327
+ isNil(err, t)
328
+ equals(len(b), n, t)
329
+
330
+ existsWithLen(filename, n, t)
331
+ fileCount(dir, 1, t)
332
+
333
+ // two days later
334
+ newFakeTime()
335
+
336
+ b2 := []byte("foooooo!")
337
+ n, err = l.Write(b2)
338
+ isNil(err, t)
339
+ equals(len(b2), n, t)
340
+ existsWithLen(backupFile(dir), len(b), t)
341
+
342
+ // we need to wait a little bit since the files get deleted on a different
343
+ // goroutine.
344
+ <-time.After(10 * time.Millisecond)
345
+
346
+ // We should still have 2 log files, since the most recent backup was just
347
+ // created.
348
+ fileCount(dir, 2, t)
349
+
350
+ existsWithLen(filename, len(b2), t)
351
+
352
+ // we should have deleted the old file due to being too old
353
+ existsWithLen(backupFile(dir), len(b), t)
354
+
355
+ // two days later
356
+ newFakeTime()
357
+
358
+ b3 := []byte("foooooo!")
359
+ n, err = l.Write(b2)
360
+ isNil(err, t)
361
+ equals(len(b3), n, t)
362
+ existsWithLen(backupFile(dir), len(b2), t)
363
+
364
+ // we need to wait a little bit since the files get deleted on a different
365
+ // goroutine.
366
+ <-time.After(10 * time.Millisecond)
367
+
368
+ // We should have 2 log files - the main log file, and the most recent
369
+ // backup. The earlier backup is past the cutoff and should be gone.
370
+ fileCount(dir, 2, t)
371
+
372
+ existsWithLen(filename, len(b3), t)
373
+
374
+ // we should have deleted the old file due to being too old
375
+ existsWithLen(backupFile(dir), len(b2), t)
376
+
377
+}
378
+
379
+func TestOldLogFiles(t *testing.T) {
380
+ currentTime = fakeTime
381
+ megabyte = 1
382
+
383
+ dir := makeTempDir("TestOldLogFiles", t)
384
+ defer os.RemoveAll(dir)
385
+
386
+ filename := logFile(dir)
387
+ data := []byte("data")
388
+ err := ioutil.WriteFile(filename, data, 07)
389
+ isNil(err, t)
390
+
391
+ // This gives us a time with the same precision as the time we get from the
392
+ // timestamp in the name.
393
+ t1, err := time.Parse(backupTimeFormat, fakeTime().UTC().Format(backupTimeFormat))
394
+ isNil(err, t)
395
+
396
+ backup := backupFile(dir)
397
+ err = ioutil.WriteFile(backup, data, 07)
398
+ isNil(err, t)
399
+
400
+ newFakeTime()
401
+
402
+ t2, err := time.Parse(backupTimeFormat, fakeTime().UTC().Format(backupTimeFormat))
403
+ isNil(err, t)
404
+
405
+ backup2 := backupFile(dir)
406
+ err = ioutil.WriteFile(backup2, data, 07)
407
+ isNil(err, t)
408
+
409
+ l := &Logger{Filename: filename}
410
+ files, err := l.oldLogFiles()
411
+ isNil(err, t)
412
+ equals(2, len(files), t)
413
+
414
+ // should be sorted by newest file first, which would be t2
415
+ equals(t2, files[0].timestamp, t)
416
+ equals(t1, files[1].timestamp, t)
417
+}
418
+
419
+func TestTimeFromName(t *testing.T) {
420
+ l := &Logger{Filename: "/var/log/myfoo/foo.log"}
421
+ prefix, ext := l.prefixAndExt()
422
+ val := l.timeFromName("foo-2014-05-04T14-44-33.555.log", prefix, ext)
423
+ equals("2014-05-04T14-44-33.555", val, t)
424
+
425
+ val = l.timeFromName("foo-2014-05-04T14-44-33.555", prefix, ext)
426
+ equals("", val, t)
427
+
428
+ val = l.timeFromName("2014-05-04T14-44-33.555.log", prefix, ext)
429
+ equals("", val, t)
430
+
431
+ val = l.timeFromName("foo.log", prefix, ext)
432
+ equals("", val, t)
433
+}
434
+
435
+func TestLocalTime(t *testing.T) {
436
+ currentTime = fakeTime
437
+ megabyte = 1
438
+
439
+ dir := makeTempDir("TestLocalTime", t)
440
+ defer os.RemoveAll(dir)
441
+
442
+ l := &Logger{
443
+ Filename: logFile(dir),
444
+ MaxSize: 10,
445
+ LocalTime: true,
446
+ }
447
+ defer l.Close()
448
+ b := []byte("boo!")
449
+ n, err := l.Write(b)
450
+ isNil(err, t)
451
+ equals(len(b), n, t)
452
+
453
+ b2 := []byte("fooooooo!")
454
+ n2, err := l.Write(b2)
455
+ isNil(err, t)
456
+ equals(len(b2), n2, t)
457
+
458
+ existsWithLen(logFile(dir), n2, t)
459
+ existsWithLen(backupFileLocal(dir), n, t)
460
+}
461
+
462
+func TestRotate(t *testing.T) {
463
+ currentTime = fakeTime
464
+ dir := makeTempDir("TestRotate", t)
465
+ defer os.RemoveAll(dir)
466
+
467
+ filename := logFile(dir)
468
+
469
+ l := &Logger{
470
+ Filename: filename,
471
+ MaxBackups: 1,
472
+ MaxSize: 100, // megabytes
473
+ }
474
+ defer l.Close()
475
+ b := []byte("boo!")
476
+ n, err := l.Write(b)
477
+ isNil(err, t)
478
+ equals(len(b), n, t)
479
+
480
+ existsWithLen(filename, n, t)
481
+ fileCount(dir, 1, t)
482
+
483
+ newFakeTime()
484
+
485
+ err = l.Rotate()
486
+ isNil(err, t)
487
+
488
+ // we need to wait a little bit since the files get deleted on a different
489
+ // goroutine.
490
+ <-time.After(10 * time.Millisecond)
491
+
492
+ filename2 := backupFile(dir)
493
+ existsWithLen(filename2, n, t)
494
+ existsWithLen(filename, 0, t)
495
+ fileCount(dir, 2, t)
496
+ newFakeTime()
497
+
498
+ err = l.Rotate()
499
+ isNil(err, t)
500
+
501
+ // we need to wait a little bit since the files get deleted on a different
502
+ // goroutine.
503
+ <-time.After(10 * time.Millisecond)
504
+
505
+ filename3 := backupFile(dir)
506
+ existsWithLen(filename3, 0, t)
507
+ existsWithLen(filename, 0, t)
508
+ fileCount(dir, 2, t)
509
+
510
+ b2 := []byte("foooooo!")
511
+ n, err = l.Write(b2)
512
+ isNil(err, t)
513
+ equals(len(b2), n, t)
514
+
515
+ // this will use the new fake time
516
+ existsWithLen(filename, n, t)
517
+}
518
+
519
+func TestJson(t *testing.T) {
520
+ data := []byte(`
521
+{
522
+ "filename": "foo",
523
+ "maxsize": 5,
524
+ "maxage": 10,
525
+ "maxbackups": 3,
526
+ "localtime": true
527
+}`[1:])
528
+
529
+ l := Logger{}
530
+ err := json.Unmarshal(data, &l)
531
+ isNil(err, t)
532
+ equals("foo", l.Filename, t)
533
+ equals(5, l.MaxSize, t)
534
+ equals(10, l.MaxAge, t)
535
+ equals(3, l.MaxBackups, t)
536
+ equals(true, l.LocalTime, t)
537
+}
538
+
539
+func TestYaml(t *testing.T) {
540
+ data := []byte(`
541
+filename: foo
542
+maxsize: 5
543
+maxage: 10
544
+maxbackups: 3
545
+localtime: true`[1:])
546
+
547
+ l := Logger{}
548
+ err := yaml.Unmarshal(data, &l)
549
+ isNil(err, t)
550
+ equals("foo", l.Filename, t)
551
+ equals(5, l.MaxSize, t)
552
+ equals(10, l.MaxAge, t)
553
+ equals(3, l.MaxBackups, t)
554
+ equals(true, l.LocalTime, t)
555
+}
556
+
557
+func TestToml(t *testing.T) {
558
+ data := `
559
+filename = "foo"
560
+maxsize = 5
561
+maxage = 10
562
+maxbackups = 3
563
+localtime = true`[1:]
564
+
565
+ l := Logger{}
566
+ md, err := toml.Decode(data, &l)
567
+ isNil(err, t)
568
+ equals("foo", l.Filename, t)
569
+ equals(5, l.MaxSize, t)
570
+ equals(10, l.MaxAge, t)
571
+ equals(3, l.MaxBackups, t)
572
+ equals(true, l.LocalTime, t)
573
+ equals(0, len(md.Undecoded()), t)
574
+}
575
+
576
+// makeTempDir creates a file with a semi-unique name in the OS temp directory.
577
+// It should be based on the name of the test, to keep parallel tests from
578
+// colliding, and must be cleaned up after the test is finished.
579
+func makeTempDir(name string, t testing.TB) string {
580
+ dir := time.Now().Format(name + backupTimeFormat)
581
+ dir = filepath.Join(os.TempDir(), dir)
582
+ isNilUp(os.Mkdir(dir, 0777), t, 1)
583
+ return dir
584
+}
585
+
586
+// existsWithLen checks that the given file exists and has the correct length.
587
+func existsWithLen(path string, length int, t testing.TB) {
588
+ info, err := os.Stat(path)
589
+ isNilUp(err, t, 1)
590
+ equalsUp(int64(length), info.Size(), t, 1)
591
+}
592
+
593
+// logFile returns the log file name in the given directory for the current fake
594
+// time.
595
+func logFile(dir string) string {
596
+ return filepath.Join(dir, "foobar.log")
597
+}
598
+
599
+func backupFile(dir string) string {
600
+ return filepath.Join(dir, "foobar-"+fakeTime().UTC().Format(backupTimeFormat)+".log")
601
+}
602
+
603
+func backupFileLocal(dir string) string {
604
+ return filepath.Join(dir, "foobar-"+fakeTime().Format(backupTimeFormat)+".log")
605
+}
606
+
607
+// logFileLocal returns the log file name in the given directory for the current
608
+// fake time using the local timezone.
609
+func logFileLocal(dir string) string {
610
+ return filepath.Join(dir, fakeTime().Format(backupTimeFormat))
611
+}
612
+
613
+// fileCount checks that the number of files in the directory is exp.
614
+func fileCount(dir string, exp int, t testing.TB) {
615
+ files, err := ioutil.ReadDir(dir)
616
+ isNilUp(err, t, 1)
617
+ // Make sure no other files were created.
618
+ equalsUp(exp, len(files), t, 1)
619
+}
620
+
621
+// newFakeTime sets the fake "current time" to two days later.
622
+func newFakeTime() {
623
+ fakeCurrentTime = fakeCurrentTime.Add(time.Hour * 24 * 2)
624
+}
625
+
626
+func notExist(path string, t testing.TB) {
627
+ _, err := os.Stat(path)
628
+ assertUp(os.IsNotExist(err), t, 1, "expected to get os.IsNotExist, but instead got %v", err)
629
+}
630
+
631
+func exists(path string, t testing.TB) {
632
+ _, err := os.Stat(path)
633
+ assertUp(err == nil, t, 1, "expected file to exist, but got error from os.Stat: %v", err)
634
+}
Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/rotate_test.go
new
+27
@@ -0,0 +1,27 @@
1
+// +build linux
2
+
3
+package lumberjack_test
4
+
5
+import (
6
+ "log"
7
+ "os"
8
+ "os/signal"
9
+ "syscall"
10
+
11
+ "github.com/natefinch/lumberjack"
12
+)
13
+
14
+// Example of how to rotate in response to SIGHUP.
15
+func ExampleLogger_Rotate() {
16
+ l := &lumberjack.Logger{}
17
+ log.SetOutput(l)
18
+ c := make(chan os.Signal, 1)
19
+ signal.Notify(c, syscall.SIGHUP)
20
+
21
+ go func() {
22
+ for {
23
+ <-c
24
+ l.Rotate()
25
+ }
26
+ }()
27
+}
Godeps/_workspace/src/gopkg.in/natefinch/lumberjack.v2/testing_test.go
new
+91
@@ -0,0 +1,91 @@
1
+package lumberjack
2
+
3
+import (
4
+ "fmt"
5
+ "path/filepath"
6
+ "reflect"
7
+ "runtime"
8
+ "testing"
9
+)
10
+
11
+// assert will log the given message if condition is false.
12
+func assert(condition bool, t testing.TB, msg string, v ...interface{}) {
13
+ assertUp(condition, t, 1, msg, v...)
14
+}
15
+
16
+// assertUp is like assert, but used inside helper functions, to ensure that
17
+// the file and line number reported by failures corresponds to one or more
18
+// levels up the stack.
19
+func assertUp(condition bool, t testing.TB, caller int, msg string, v ...interface{}) {
20
+ if !condition {
21
+ _, file, line, _ := runtime.Caller(caller + 1)
22
+ v = append([]interface{}{filepath.Base(file), line}, v...)
23
+ fmt.Printf("%s:%d: "+msg+"\n", v...)
24
+ t.FailNow()
25
+ }
26
+}
27
+
28
+// equals tests that the two values are equal according to reflect.DeepEqual.
29
+func equals(exp, act interface{}, t testing.TB) {
30
+ equalsUp(exp, act, t, 1)
31
+}
32
+
33
+// equalsUp is like equals, but used inside helper functions, to ensure that the
34
+// file and line number reported by failures corresponds to one or more levels
35
+// up the stack.
36
+func equalsUp(exp, act interface{}, t testing.TB, caller int) {
37
+ if !reflect.DeepEqual(exp, act) {
38
+ _, file, line, _ := runtime.Caller(caller + 1)
39
+ fmt.Printf("%s:%d: exp: %v (%T), got: %v (%T)\n",
40
+ filepath.Base(file), line, exp, exp, act, act)
41
+ t.FailNow()
42
+ }
43
+}
44
+
45
+// isNil reports a failure if the given value is not nil. Note that values
46
+// which cannot be nil will always fail this check.
47
+func isNil(obtained interface{}, t testing.TB) {
48
+ isNilUp(obtained, t, 1)
49
+}
50
+
51
+// isNilUp is like isNil, but used inside helper functions, to ensure that the
52
+// file and line number reported by failures corresponds to one or more levels
53
+// up the stack.
54
+func isNilUp(obtained interface{}, t testing.TB, caller int) {
55
+ if !_isNil(obtained) {
56
+ _, file, line, _ := runtime.Caller(caller + 1)
57
+ fmt.Printf("%s:%d: expected nil, got: %v\n", filepath.Base(file), line, obtained)
58
+ t.FailNow()
59
+ }
60
+}
61
+
62
+// notNil reports a failure if the given value is nil.
63
+func notNil(obtained interface{}, t testing.TB) {
64
+ notNilUp(obtained, t, 1)
65
+}
66
+
67
+// notNilUp is like notNil, but used inside helper functions, to ensure that the
68
+// file and line number reported by failures corresponds to one or more levels
69
+// up the stack.
70
+func notNilUp(obtained interface{}, t testing.TB, caller int) {
71
+ if _isNil(obtained) {
72
+ _, file, line, _ := runtime.Caller(caller + 1)
73
+ fmt.Printf("%s:%d: expected non-nil, got: %v\n", filepath.Base(file), line, obtained)
74
+ t.FailNow()
75
+ }
76
+}
77
+
78
+// _isNil is a helper function for isNil and notNil, and should not be used
79
+// directly.
80
+func _isNil(obtained interface{}) bool {
81
+ if obtained == nil {
82
+ return true
83
+ }
84
+
85
+ switch v := reflect.ValueOf(obtained); v.Kind() {
86
+ case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Ptr, reflect.Slice:
87
+ return v.IsNil()
88
+ }
89
+
90
+ return false
91
+}