@cryptotaxi247 / kubo / commits / 2b72d9f14

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 [![GoDoc](https://godoc.org/gopkg.in/natefinch/lumberjack.v2?status.png)](https://godoc.org/gopkg.in/natefinch/lumberjack.v2) [![Build Status](https://drone.io/github.com/natefinch/lumberjack/status.png)](https://drone.io/github.com/natefinch/lumberjack/latest) [![Build status](https://ci.appveyor.com/api/projects/status/00gchpxtg4gkrt5d)](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 +}