OSDN Git Service

log into file (#357)
[bytom/vapor.git] / vendor / github.com / lestrrat-go / file-rotatelogs / rotatelogs_test.go
1 package rotatelogs_test
2
3 import (
4         "fmt"
5         "io"
6         "io/ioutil"
7         "log"
8         "os"
9         "path/filepath"
10         "strings"
11         "testing"
12         "time"
13
14         "github.com/jonboulle/clockwork"
15         rotatelogs "github.com/lestrrat-go/file-rotatelogs"
16         "github.com/pkg/errors"
17         "github.com/stretchr/testify/assert"
18 )
19
20 func TestSatisfiesIOWriter(t *testing.T) {
21         var w io.Writer
22         w, _ = rotatelogs.New("/foo/bar")
23         _ = w
24 }
25
26 func TestSatisfiesIOCloser(t *testing.T) {
27         var c io.Closer
28         c, _ = rotatelogs.New("/foo/bar")
29         _ = c
30 }
31
32 func TestLogRotate(t *testing.T) {
33         dir, err := ioutil.TempDir("", "file-rotatelogs-test")
34         if !assert.NoError(t, err, "creating temporary directory should succeed") {
35                 return
36         }
37         defer os.RemoveAll(dir)
38
39         // Change current time, so we can safely purge old logs
40         dummyTime := time.Now().Add(-7 * 24 * time.Hour)
41         dummyTime = dummyTime.Add(time.Duration(-1 * dummyTime.Nanosecond()))
42         clock := clockwork.NewFakeClockAt(dummyTime)
43         linkName := filepath.Join(dir, "log")
44         rl, err := rotatelogs.New(
45                 filepath.Join(dir, "log%Y%m%d%H%M%S"),
46                 rotatelogs.WithClock(clock),
47                 rotatelogs.WithMaxAge(24*time.Hour),
48                 rotatelogs.WithLinkName(linkName),
49         )
50         if !assert.NoError(t, err, `rotatelogs.New should succeed`) {
51                 return
52         }
53         defer rl.Close()
54
55         str := "Hello, World"
56         n, err := rl.Write([]byte(str))
57         if !assert.NoError(t, err, "rl.Write should succeed") {
58                 return
59         }
60
61         if !assert.Len(t, str, n, "rl.Write should succeed") {
62                 return
63         }
64
65         fn := rl.CurrentFileName()
66         if fn == "" {
67                 t.Errorf("Could not get filename %s", fn)
68         }
69
70         content, err := ioutil.ReadFile(fn)
71         if err != nil {
72                 t.Errorf("Failed to read file %s: %s", fn, err)
73         }
74
75         if string(content) != str {
76                 t.Errorf(`File content does not match (was "%s")`, content)
77         }
78
79         err = os.Chtimes(fn, dummyTime, dummyTime)
80         if err != nil {
81                 t.Errorf("Failed to change access/modification times for %s: %s", fn, err)
82         }
83
84         fi, err := os.Stat(fn)
85         if err != nil {
86                 t.Errorf("Failed to stat %s: %s", fn, err)
87         }
88
89         if !fi.ModTime().Equal(dummyTime) {
90                 t.Errorf("Failed to chtime for %s (expected %s, got %s)", fn, fi.ModTime(), dummyTime)
91         }
92
93         clock.Advance(time.Duration(7 * 24 * time.Hour))
94
95         // This next Write() should trigger Rotate()
96         rl.Write([]byte(str))
97         newfn := rl.CurrentFileName()
98         if newfn == fn {
99                 t.Errorf(`New file name and old file name should not match ("%s" != "%s")`, fn, newfn)
100         }
101
102         content, err = ioutil.ReadFile(newfn)
103         if err != nil {
104                 t.Errorf("Failed to read file %s: %s", newfn, err)
105         }
106
107         if string(content) != str {
108                 t.Errorf(`File content does not match (was "%s")`, content)
109         }
110
111         time.Sleep(time.Second)
112
113         // fn was declared above, before mocking CurrentTime
114         // Old files should have been unlinked
115         _, err = os.Stat(fn)
116         if !assert.Error(t, err, "os.Stat should have failed") {
117                 return
118         }
119
120         linkDest, err := os.Readlink(linkName)
121         if err != nil {
122                 t.Errorf("Failed to readlink %s: %s", linkName, err)
123         }
124
125         if linkDest != newfn {
126                 t.Errorf(`Symlink destination does not match expected filename ("%s" != "%s")`, newfn, linkDest)
127         }
128 }
129
130 func CreateRotationTestFile(dir string, base time.Time, d time.Duration, n int) {
131         timestamp := base
132         for i := 0; i < n; i++ {
133                 // %Y%m%d%H%M%S
134                 suffix := timestamp.Format("20060102150405")
135                 path := filepath.Join(dir, "log"+suffix)
136                 ioutil.WriteFile(path, []byte("rotation test file\n"), os.ModePerm)
137                 os.Chtimes(path, timestamp, timestamp)
138                 timestamp = timestamp.Add(d)
139         }
140 }
141
142 func TestLogRotationCount(t *testing.T) {
143         dir, err := ioutil.TempDir("", "file-rotatelogs-rotationcount-test")
144         if !assert.NoError(t, err, "creating temporary directory should succeed") {
145                 return
146         }
147         defer os.RemoveAll(dir)
148
149         dummyTime := time.Now().Add(-7 * 24 * time.Hour)
150         dummyTime = dummyTime.Add(time.Duration(-1 * dummyTime.Nanosecond()))
151         clock := clockwork.NewFakeClockAt(dummyTime)
152
153         t.Run("Either maxAge or rotationCount should be set", func(t *testing.T) {
154                 rl, err := rotatelogs.New(
155                         filepath.Join(dir, "log%Y%m%d%H%M%S"),
156                         rotatelogs.WithClock(clock),
157                         rotatelogs.WithMaxAge(time.Duration(0)),
158                         rotatelogs.WithRotationCount(0),
159                 )
160                 if !assert.NoError(t, err, `Both of maxAge and rotationCount is disabled`) {
161                         return
162                 }
163                 defer rl.Close()
164         })
165
166         t.Run("Either maxAge or rotationCount should be set", func(t *testing.T) {
167                 rl, err := rotatelogs.New(
168                         filepath.Join(dir, "log%Y%m%d%H%M%S"),
169                         rotatelogs.WithClock(clock),
170                         rotatelogs.WithMaxAge(1),
171                         rotatelogs.WithRotationCount(1),
172                 )
173                 if !assert.Error(t, err, `Both of maxAge and rotationCount is enabled`) {
174                         return
175                 }
176                 if rl != nil {
177                         defer rl.Close()
178                 }
179         })
180
181         t.Run("Only latest log file is kept", func(t *testing.T) {
182                 rl, err := rotatelogs.New(
183                         filepath.Join(dir, "log%Y%m%d%H%M%S"),
184                         rotatelogs.WithClock(clock),
185                         rotatelogs.WithMaxAge(-1),
186                         rotatelogs.WithRotationCount(1),
187                 )
188                 if !assert.NoError(t, err, `rotatelogs.New should succeed`) {
189                         return
190                 }
191                 defer rl.Close()
192
193                 n, err := rl.Write([]byte("dummy"))
194                 if !assert.NoError(t, err, "rl.Write should succeed") {
195                         return
196                 }
197                 if !assert.Len(t, "dummy", n, "rl.Write should succeed") {
198                         return
199                 }
200                 time.Sleep(time.Second)
201                 files, err := filepath.Glob(filepath.Join(dir, "log*"))
202                 if !assert.Equal(t, 1, len(files), "Only latest log is kept") {
203                         return
204                 }
205         })
206
207         t.Run("Old log files are purged except 2 log files", func(t *testing.T) {
208                 CreateRotationTestFile(dir, dummyTime, time.Duration(time.Hour), 5)
209                 rl, err := rotatelogs.New(
210                         filepath.Join(dir, "log%Y%m%d%H%M%S"),
211                         rotatelogs.WithClock(clock),
212                         rotatelogs.WithMaxAge(-1),
213                         rotatelogs.WithRotationCount(2),
214                 )
215                 if !assert.NoError(t, err, `rotatelogs.New should succeed`) {
216                         return
217                 }
218                 defer rl.Close()
219
220                 n, err := rl.Write([]byte("dummy"))
221                 if !assert.NoError(t, err, "rl.Write should succeed") {
222                         return
223                 }
224                 if !assert.Len(t, "dummy", n, "rl.Write should succeed") {
225                         return
226                 }
227                 time.Sleep(time.Second)
228                 files, err := filepath.Glob(filepath.Join(dir, "log*"))
229                 if !assert.Equal(t, 2, len(files), "One file is kept") {
230                         return
231                 }
232         })
233
234 }
235
236 func TestLogSetOutput(t *testing.T) {
237         dir, err := ioutil.TempDir("", "file-rotatelogs-test")
238         if err != nil {
239                 t.Errorf("Failed to create temporary directory: %s", err)
240         }
241         defer os.RemoveAll(dir)
242
243         rl, err := rotatelogs.New(filepath.Join(dir, "log%Y%m%d%H%M%S"))
244         if !assert.NoError(t, err, `rotatelogs.New should succeed`) {
245                 return
246         }
247         defer rl.Close()
248
249         log.SetOutput(rl)
250         defer log.SetOutput(os.Stderr)
251
252         str := "Hello, World"
253         log.Print(str)
254
255         fn := rl.CurrentFileName()
256         if fn == "" {
257                 t.Errorf("Could not get filename %s", fn)
258         }
259
260         content, err := ioutil.ReadFile(fn)
261         if err != nil {
262                 t.Errorf("Failed to read file %s: %s", fn, err)
263         }
264
265         if !strings.Contains(string(content), str) {
266                 t.Errorf(`File content does not contain "%s" (was "%s")`, str, content)
267         }
268 }
269
270 func TestGHIssue16(t *testing.T) {
271         defer func() {
272                 if v := recover(); v != nil {
273                         assert.NoError(t, errors.Errorf("%s", v), "error should be nil")
274                 }
275         }()
276
277         dir, err := ioutil.TempDir("", "file-rotatelogs-gh16")
278         if !assert.NoError(t, err, `creating temporary directory should succeed`) {
279                 return
280         }
281         defer os.RemoveAll(dir)
282
283         rl, err := rotatelogs.New(
284                 filepath.Join(dir, "log%Y%m%d%H%M%S"),
285                 rotatelogs.WithLinkName("./test.log"),
286                 rotatelogs.WithRotationTime(10*time.Second),
287                 rotatelogs.WithRotationCount(3),
288                 rotatelogs.WithMaxAge(-1),
289         )
290         if !assert.NoError(t, err, `rotatelogs.New should succeed`) {
291                 return
292         }
293
294         if !assert.NoError(t, rl.Rotate(), "rl.Rotate should succeed") {
295                 return
296         }
297         defer rl.Close()
298 }
299
300 func TestRotationGenerationalNames(t *testing.T) {
301         dir, err := ioutil.TempDir("", "file-rotatelogs-generational")
302         if !assert.NoError(t, err, `creating temporary directory should succeed`) {
303                 return
304         }
305         defer os.RemoveAll(dir)
306
307         t.Run("Rotate over unchanged pattern", func(t *testing.T) {
308                 rl, err := rotatelogs.New(
309                         filepath.Join(dir, "unchanged-pattern.log"),
310                 )
311                 if !assert.NoError(t, err, `rotatelogs.New should succeed`) {
312                         return
313                 }
314
315                 seen := map[string]struct{}{}
316                 for i := 0; i < 10; i++ {
317                         rl.Write([]byte("Hello, World!"))
318                         if !assert.NoError(t, rl.Rotate(), "rl.Rotate should succeed") {
319                                 return
320                         }
321
322                         // Because every call to Rotate should yield a new log file,
323                         // and the previous files already exist, the filenames should share
324                         // the same prefix and have a unique suffix
325                         fn := filepath.Base(rl.CurrentFileName())
326                         if !assert.True(t, strings.HasPrefix(fn, "unchanged-pattern.log"), "prefix for all filenames should match") {
327                                 return
328                         }
329                         rl.Write([]byte("Hello, World!"))
330                         suffix := strings.TrimPrefix(fn, "unchanged-pattern.log")
331                         expectedSuffix := fmt.Sprintf(".%d", i+1)
332                         if !assert.True(t, suffix == expectedSuffix, "expected suffix %s found %s", expectedSuffix, suffix) {
333                                 return
334                         }
335                         assert.FileExists(t, rl.CurrentFileName(), "file does not exist %s", rl.CurrentFileName())
336                         stat, err := os.Stat(rl.CurrentFileName())
337                         if err == nil {
338                                 if !assert.True(t, stat.Size() == 13, "file %s size is %d, expected 13", rl.CurrentFileName(), stat.Size()) {
339                                         return
340                                 }
341                         } else {
342                                 assert.Failf(t, "could not stat file %s", rl.CurrentFileName())
343                                 return
344                         }
345
346                         if _, ok := seen[suffix]; !assert.False(t, ok, `filename suffix %s should be unique`, suffix) {
347                                 return
348                         }
349                         seen[suffix] = struct{}{}
350                 }
351                 defer rl.Close()
352         })
353         t.Run("Rotate over pattern change over every second", func(t *testing.T) {
354                 rl, err := rotatelogs.New(
355                         filepath.Join(dir, "every-second-pattern-%Y%m%d%H%M%S.log"),
356                         rotatelogs.WithRotationTime(time.Nanosecond),
357                 )
358                 if !assert.NoError(t, err, `rotatelogs.New should succeed`) {
359                         return
360                 }
361
362                 for i := 0; i < 10; i++ {
363                         time.Sleep(time.Second)
364                         rl.Write([]byte("Hello, World!"))
365                         if !assert.NoError(t, rl.Rotate(), "rl.Rotate should succeed") {
366                                 return
367                         }
368
369                         // because every new Write should yield a new logfile,
370                         // every rorate should be create a filename ending with a .1
371                         if !assert.True(t, strings.HasSuffix(rl.CurrentFileName(), ".1"), "log name should end with .1") {
372                                 return
373                         }
374                 }
375                 defer rl.Close()
376         })
377 }
378
379 type ClockFunc func() time.Time
380
381 func (f ClockFunc) Now() time.Time {
382         return f()
383 }
384
385 func TestGHIssue23(t *testing.T) {
386         dir, err := ioutil.TempDir("", "file-rotatelogs-generational")
387         if !assert.NoError(t, err, `creating temporary directory should succeed`) {
388                 return
389         }
390         defer os.RemoveAll(dir)
391
392         for _, locName := range []string{"Asia/Tokyo", "Pacific/Honolulu"} {
393                 loc, _ := time.LoadLocation(locName)
394                 tests := []struct {
395                         Expected string
396                         Clock    rotatelogs.Clock
397                 }{
398                         {
399                                 Expected: filepath.Join(dir, strings.ToLower(strings.Replace(locName, "/", "_", -1)) + ".201806010000.log"),
400                                 Clock: ClockFunc(func() time.Time {
401                                         return time.Date(2018, 6, 1, 3, 18, 0, 0, loc)
402                                 }),
403                         },
404                         {
405                                 Expected: filepath.Join(dir, strings.ToLower(strings.Replace(locName, "/", "_", -1)) + ".201712310000.log"),
406                                 Clock: ClockFunc(func() time.Time {
407                                         return time.Date(2017, 12, 31, 23, 52, 0, 0, loc)
408                                 }),
409                         },
410                 }
411                 for _, test := range tests {
412                         t.Run(fmt.Sprintf("location = %s, time = %s", locName, test.Clock.Now().Format(time.RFC3339)), func(t *testing.T) {
413                                 template := strings.ToLower(strings.Replace(locName, "/", "_", -1)) + ".%Y%m%d%H%M.log"
414                                 rl, err := rotatelogs.New(
415                                         filepath.Join(dir, template),
416                                         rotatelogs.WithClock(test.Clock), // we're not using WithLocation, but it's the same thing
417                                 )
418                                 if !assert.NoError(t, err, "rotatelogs.New should succeed") {
419                                         return
420                                 }
421
422                                 t.Logf("expected %s", test.Expected)
423                                 rl.Rotate()
424                                 if !assert.Equal(t, test.Expected, rl.CurrentFileName(), "file names should match") {
425                                         return
426                                 }
427                         })
428                 }
429         }
430 }
431
432 func TestForceNewFile(t *testing.T) {
433         dir, err := ioutil.TempDir("", "file-rotatelogs-force-new-file")
434         if !assert.NoError(t, err, `creating temporary directory should succeed`) {
435                 return
436         }
437         defer os.RemoveAll(dir)
438
439         t.Run("Force a new file", func(t *testing.T) {
440
441                 rl, err := rotatelogs.New(
442                         filepath.Join(dir, "force-new-file.log"),
443                         rotatelogs.ForceNewFile(),
444                 )
445                 if !assert.NoError(t, err, "rotatelogs.New should succeed") {
446                         return
447                 }
448                 rl.Write([]byte("Hello, World!"))
449                 rl.Close()
450
451                 for i := 0; i < 10; i++ {
452                         baseFn := filepath.Join(dir, "force-new-file.log")
453                         rl, err := rotatelogs.New(
454                                 baseFn,
455                                 rotatelogs.ForceNewFile(),
456                         )
457                         if !assert.NoError(t, err, "rotatelogs.New should succeed") {
458                                 return
459                         }
460                         rl.Write([]byte("Hello, World"))
461                         rl.Write([]byte(fmt.Sprintf("%d", i)))
462                         rl.Close()
463
464                         fn := filepath.Base(rl.CurrentFileName())
465                         suffix := strings.TrimPrefix(fn, "force-new-file.log")
466                         expectedSuffix := fmt.Sprintf(".%d", i+1)
467                         if !assert.True(t, suffix == expectedSuffix, "expected suffix %s found %s", expectedSuffix, suffix) {
468                                 return
469                         }
470                         assert.FileExists(t, rl.CurrentFileName(), "file does not exist %s", rl.CurrentFileName())
471                         content, err := ioutil.ReadFile(rl.CurrentFileName())
472                         if !assert.NoError(t, err, "ioutil.ReadFile %s should succeed", rl.CurrentFileName()) {
473                                 return
474                         }
475                         str := fmt.Sprintf("Hello, World%d", i)
476                         if !assert.Equal(t, str, string(content), "read %s from file %s, not expected %s", string(content), rl.CurrentFileName(), str) {
477                                 return
478                         }
479
480                         assert.FileExists(t, baseFn, "file does not exist %s", baseFn)
481                         content, err = ioutil.ReadFile(baseFn)
482                         if !assert.NoError(t, err, "ioutil.ReadFile should succeed") {
483                                 return
484                         }
485                         if !assert.Equal(t, "Hello, World!", string(content), "read %s from file %s, not expected Hello, World!", string(content), baseFn) {
486                                 return
487                         }
488                 }
489
490                 })
491
492         t.Run("Force a new file with Rotate", func(t *testing.T) {
493
494                 baseFn := filepath.Join(dir, "force-new-file-rotate.log")
495                 rl, err := rotatelogs.New(
496                         baseFn,
497                         rotatelogs.ForceNewFile(),
498                 )
499                 if !assert.NoError(t, err, "rotatelogs.New should succeed") {
500                         return
501                 }
502                 rl.Write([]byte("Hello, World!"))
503
504                 for i := 0; i < 10; i++ {
505                         if !assert.NoError(t, rl.Rotate(), "rl.Rotate should succeed") {
506                                 return
507                         }
508                         rl.Write([]byte("Hello, World"))
509                         rl.Write([]byte(fmt.Sprintf("%d", i)))
510                         assert.FileExists(t, rl.CurrentFileName(), "file does not exist %s", rl.CurrentFileName())
511                         content, err := ioutil.ReadFile(rl.CurrentFileName())
512                         if !assert.NoError(t, err, "ioutil.ReadFile %s should succeed", rl.CurrentFileName()) {
513                                 return
514                         }
515                         str := fmt.Sprintf("Hello, World%d", i)
516                         if !assert.Equal(t, str, string(content), "read %s from file %s, not expected %s", string(content), rl.CurrentFileName(), str) {
517                                 return
518                         }
519
520                         assert.FileExists(t, baseFn, "file does not exist %s", baseFn)
521                         content, err = ioutil.ReadFile(baseFn)
522                         if !assert.NoError(t, err, "ioutil.ReadFile should succeed") {
523                                 return
524                         }
525                         if !assert.Equal(t, "Hello, World!", string(content), "read %s from file %s, not expected Hello, World!", string(content), baseFn) {
526                                 return
527                         }
528                 }
529         })
530 }
531