Skip to content
This repository has been archived by the owner on Jul 19, 2021. It is now read-only.

Commit

Permalink
Merge pull request #39 from bobmcn/hotfix/force-new-file
Browse files Browse the repository at this point in the history
Add an option to New that will always create a new file.
  • Loading branch information
lestrrat authored Jul 18, 2019
2 parents cf4bc19 + e92e07d commit e0a88b0
Show file tree
Hide file tree
Showing 6 changed files with 214 additions and 6 deletions.
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,30 @@ object. Currently only supported event type is FiledRotated
)
```

## ForceNewFile

Ensure a new file is created every time New() is called. If the base file name
already exists, an implicit rotation is performed.

```go
rotatelogs.New(
"/var/log/myapp/log.%Y%m%d",
rotatelogs.ForceNewFile(),
)
```

## ForceNewFile

Ensure a new file is created every time New() is called. If the base file name
already exists, an implicit rotation is performed.

```go
rotatelogs.New(
"/var/log/myapp/log.%Y%m%d",
rotatelogs.ForceNewFile(),
)
```

# Rotating files forcefully

If you want to rotate files forcefully before the actual rotation time has reached,
Expand Down
56 changes: 56 additions & 0 deletions example_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package rotatelogs_test

import (
"fmt"
"io/ioutil"
"os"
rotatelogs "github.com/lestrrat-go/file-rotatelogs"
)

func ExampleForceNewFile () {
logDir, err := ioutil.TempDir("", "rotatelogs_test")
if err != nil {
fmt.Println("could not create log directory ", err)
return
}
logPath := fmt.Sprintf("%s/test.log", logDir)

for i := 0; i < 2; i++ {
writer, err := rotatelogs.New(logPath,
rotatelogs.ForceNewFile(),
)
if err != nil {
fmt.Println("Could not open log file ", err)
return
}

n, err := writer.Write([]byte("test"))
if err != nil || n != 4 {
fmt.Println("Write failed ", err, " number written ", n)
return
}
err = writer.Close()
if err != nil {
fmt.Println("Close failed ", err)
return
}
}

files, err := ioutil.ReadDir(logDir)
if err != nil {
fmt.Println("ReadDir failed ", err)
return
}
for _, file := range files {
fmt.Println(file.Name(), file.Size())
}

err = os.RemoveAll(logDir)
if err != nil {
fmt.Println("RemoveAll failed ", err)
return
}
// OUTPUT:
// test.log 4
// test.log.1 4
}
1 change: 1 addition & 0 deletions interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ type RotateLogs struct {
pattern *strftime.Strftime
rotationTime time.Duration
rotationCount uint
forceNewFile bool
}

// Clock is the interface used by the RotateLogs
Expand Down
8 changes: 8 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const (
optkeyMaxAge = "max-age"
optkeyRotationTime = "rotation-time"
optkeyRotationCount = "rotation-count"
optkeyForceNewFile = "force-new-file"
)

// WithClock creates a new Option that sets a clock
Expand Down Expand Up @@ -72,3 +73,10 @@ func WithRotationCount(n uint) Option {
func WithHandler(h Handler) Option {
return option.New(optkeyHandler, h)
}

// ForceNewFile ensures a new file is created every time New()
// is called. If the base file name already exists, an implicit
// rotation is performed
func ForceNewFile() Option {
return option.New(optkeyForceNewFile, true)
}
30 changes: 24 additions & 6 deletions rotatelogs.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ func New(p string, options ...Option) (*RotateLogs, error) {
var linkName string
var maxAge time.Duration
var handler Handler
var forceNewFile bool

for _, o := range options {
switch o.Name() {
Expand All @@ -62,6 +63,8 @@ func New(p string, options ...Option) (*RotateLogs, error) {
rotationCount = o.Value().(uint)
case optkeyHandler:
handler = o.Value().(Handler)
case optkeyForceNewFile:
forceNewFile = true
}
}

Expand All @@ -83,6 +86,7 @@ func New(p string, options ...Option) (*RotateLogs, error) {
pattern: pattern,
rotationTime: rotationTime,
rotationCount: rotationCount,
forceNewFile: forceNewFile,
}, nil
}

Expand Down Expand Up @@ -135,24 +139,38 @@ func (rl *RotateLogs) getWriter_nolock(bailOnRotateFail, useGenerationalNames bo
// to log to, which may be newer than rl.currentFilename
baseFn := rl.genFilename()
filename := baseFn
var forceNewFile bool
if baseFn != rl.curBaseFn {
generation = 0
// even though this is the first write after calling New(),
// check if a new file needs to be created
if rl.forceNewFile {
forceNewFile = true
}
} else {
if !useGenerationalNames {
// nothing to do
return rl.outFh, nil
}
// This is used when we *REALLY* want to rotate a log.
// instead of just using the regular strftime pattern, we
// create a new file name using generational names such as
// "foo.1", "foo.2", "foo.3", etc
forceNewFile = true
generation++
}
if forceNewFile {
// A new file has been requested. Instead of just using the
// regular strftime pattern, we create a new file name using
// generational names such as "foo.1", "foo.2", "foo.3", etc
var name string
for {
generation++
name := fmt.Sprintf("%s.%d", filename, generation)
if generation == 0 {
name = filename
} else {
name = fmt.Sprintf("%s.%d", filename, generation)
}
if _, err := os.Stat(name); err != nil {
filename = name
break
}
generation++
}
}
// make sure the dir is existed, eg:
Expand Down
101 changes: 101 additions & 0 deletions rotatelogs_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -428,3 +428,104 @@ func TestGHIssue23(t *testing.T) {
}
}
}

func TestForceNewFile(t *testing.T) {
dir, err := ioutil.TempDir("", "file-rotatelogs-force-new-file")
if !assert.NoError(t, err, `creating temporary directory should succeed`) {
return
}
defer os.RemoveAll(dir)

t.Run("Force a new file", func(t *testing.T) {

rl, err := rotatelogs.New(
filepath.Join(dir, "force-new-file.log"),
rotatelogs.ForceNewFile(),
)
if !assert.NoError(t, err, "rotatelogs.New should succeed") {
return
}
rl.Write([]byte("Hello, World!"))
rl.Close()

for i := 0; i < 10; i++ {
baseFn := filepath.Join(dir, "force-new-file.log")
rl, err := rotatelogs.New(
baseFn,
rotatelogs.ForceNewFile(),
)
if !assert.NoError(t, err, "rotatelogs.New should succeed") {
return
}
rl.Write([]byte("Hello, World"))
rl.Write([]byte(fmt.Sprintf("%d", i)))
rl.Close()

fn := filepath.Base(rl.CurrentFileName())
suffix := strings.TrimPrefix(fn, "force-new-file.log")
expectedSuffix := fmt.Sprintf(".%d", i+1)
if !assert.True(t, suffix == expectedSuffix, "expected suffix %s found %s", expectedSuffix, suffix) {
return
}
assert.FileExists(t, rl.CurrentFileName(), "file does not exist %s", rl.CurrentFileName())
content, err := ioutil.ReadFile(rl.CurrentFileName())
if !assert.NoError(t, err, "ioutil.ReadFile %s should succeed", rl.CurrentFileName()) {
return
}
str := fmt.Sprintf("Hello, World%d", i)
if !assert.Equal(t, str, string(content), "read %s from file %s, not expected %s", string(content), rl.CurrentFileName(), str) {
return
}

assert.FileExists(t, baseFn, "file does not exist %s", baseFn)
content, err = ioutil.ReadFile(baseFn)
if !assert.NoError(t, err, "ioutil.ReadFile should succeed") {
return
}
if !assert.Equal(t, "Hello, World!", string(content), "read %s from file %s, not expected Hello, World!", string(content), baseFn) {
return
}
}

})

t.Run("Force a new file with Rotate", func(t *testing.T) {

baseFn := filepath.Join(dir, "force-new-file-rotate.log")
rl, err := rotatelogs.New(
baseFn,
rotatelogs.ForceNewFile(),
)
if !assert.NoError(t, err, "rotatelogs.New should succeed") {
return
}
rl.Write([]byte("Hello, World!"))

for i := 0; i < 10; i++ {
if !assert.NoError(t, rl.Rotate(), "rl.Rotate should succeed") {
return
}
rl.Write([]byte("Hello, World"))
rl.Write([]byte(fmt.Sprintf("%d", i)))
assert.FileExists(t, rl.CurrentFileName(), "file does not exist %s", rl.CurrentFileName())
content, err := ioutil.ReadFile(rl.CurrentFileName())
if !assert.NoError(t, err, "ioutil.ReadFile %s should succeed", rl.CurrentFileName()) {
return
}
str := fmt.Sprintf("Hello, World%d", i)
if !assert.Equal(t, str, string(content), "read %s from file %s, not expected %s", string(content), rl.CurrentFileName(), str) {
return
}

assert.FileExists(t, baseFn, "file does not exist %s", baseFn)
content, err = ioutil.ReadFile(baseFn)
if !assert.NoError(t, err, "ioutil.ReadFile should succeed") {
return
}
if !assert.Equal(t, "Hello, World!", string(content), "read %s from file %s, not expected Hello, World!", string(content), baseFn) {
return
}
}
})
}

0 comments on commit e0a88b0

Please sign in to comment.