@@ -22,108 +22,64 @@ func (c clockFn) Now() time.Time {
2222 return c ()
2323}
2424
25- func (o OptionFn ) Configure (rl * RotateLogs ) error {
26- return o (rl )
27- }
28-
29- // WithClock creates a new Option that sets a clock
30- // that the RotateLogs object will use to determine
31- // the current time.
32- //
33- // By default rotatelogs.Local, which returns the
34- // current time in the local time zone, is used. If you
35- // would rather use UTC, use rotatelogs.UTC as the argument
36- // to this option, and pass it to the constructor.
37- func WithClock (c Clock ) Option {
38- return OptionFn (func (rl * RotateLogs ) error {
39- rl .clock = c
40- return nil
41- })
42- }
43-
44- // WithLocation creates a new Option that sets up a
45- // "Clock" interface that the RotateLogs object will use
46- // to determine the current time.
47- //
48- // This optin works by always returning the in the given
49- // location.
50- func WithLocation (loc * time.Location ) Option {
51- return WithClock (clockFn (func () time.Time {
52- return time .Now ().In (loc )
53- }))
54- }
55-
56- // WithLinkName creates a new Option that sets the
57- // symbolic link name that gets linked to the current
58- // file name being used.
59- func WithLinkName (s string ) Option {
60- return OptionFn (func (rl * RotateLogs ) error {
61- rl .linkName = s
62- return nil
63- })
64- }
65-
66- // WithMaxAge creates a new Option that sets the
67- // max age of a log file before it gets purged from
68- // the file system.
69- func WithMaxAge (d time.Duration ) Option {
70- return OptionFn (func (rl * RotateLogs ) error {
71- if rl .rotationCount > 0 && d > 0 {
72- return errors .New ("attempt to set MaxAge when RotationCount is also given" )
73- }
74- rl .maxAge = d
75- return nil
76- })
77- }
78-
79- // WithRotationTime creates a new Option that sets the
80- // time between rotation.
81- func WithRotationTime (d time.Duration ) Option {
82- return OptionFn (func (rl * RotateLogs ) error {
83- rl .rotationTime = d
84- return nil
85- })
86- }
87-
88- // WithRotationCount creates a new Option that sets the
89- // number of files should be kept before it gets
90- // purged from the file system.
91- func WithRotationCount (n int ) Option {
92- return OptionFn (func (rl * RotateLogs ) error {
93- if rl .maxAge > 0 && n > 0 {
94- return errors .New ("attempt to set RotationCount when MaxAge is also given" )
95- }
96- rl .rotationCount = n
97- return nil
98- })
99- }
100-
10125// New creates a new RotateLogs object. A log filename pattern
10226// must be passed. Optional `Option` parameters may be passed
103- func New (pattern string , options ... Option ) (* RotateLogs , error ) {
104- globPattern := pattern
27+ func New (p string , options ... Option ) (* RotateLogs , error ) {
28+ globPattern := p
10529 for _ , re := range patternConversionRegexps {
10630 globPattern = re .ReplaceAllString (globPattern , "*" )
10731 }
10832
109- strfobj , err := strftime .New (pattern )
33+ pattern , err := strftime .New (p )
11034 if err != nil {
11135 return nil , errors .Wrap (err , `invalid strftime pattern` )
11236 }
11337
114- var rl RotateLogs
115- rl .clock = Local
116- rl .globPattern = globPattern
117- rl .pattern = strfobj
118- rl .rotationTime = 24 * time .Hour
119- // Keeping forward compatibility, maxAge is prior to rotationCount.
120- rl .maxAge = 7 * 24 * time .Hour
121- rl .rotationCount = - 1
122- for _ , opt := range options {
123- opt .Configure (& rl )
38+ var clock Clock = Local
39+ rotationTime := 24 * time .Hour
40+ var rotationCount uint
41+ var linkName string
42+ var maxAge time.Duration
43+
44+ for _ , o := range options {
45+ switch o .Name () {
46+ case optkeyClock :
47+ clock = o .Value ().(Clock )
48+ case optkeyLinkName :
49+ linkName = o .Value ().(string )
50+ case optkeyMaxAge :
51+ maxAge = o .Value ().(time.Duration )
52+ if maxAge < 0 {
53+ maxAge = 0
54+ }
55+ case optkeyRotationTime :
56+ rotationTime = o .Value ().(time.Duration )
57+ if rotationTime < 0 {
58+ rotationTime = 0
59+ }
60+ case optkeyRotationCount :
61+ rotationCount = o .Value ().(uint )
62+ }
63+ }
64+
65+ if maxAge > 0 && rotationCount > 0 {
66+ return nil , errors .New ("options MaxAge and RotationCount cannot be both set" )
67+ }
68+
69+ if maxAge == 0 && rotationCount == 0 {
70+ // if both are 0, give maxAge a sane default
71+ maxAge = 7 * 24 * time .Hour
12472 }
12573
126- return & rl , nil
74+ return & RotateLogs {
75+ clock : clock ,
76+ globPattern : globPattern ,
77+ linkName : linkName ,
78+ maxAge : maxAge ,
79+ pattern : pattern ,
80+ rotationTime : rotationTime ,
81+ rotationCount : rotationCount ,
82+ }, nil
12783}
12884
12985func (rl * RotateLogs ) genFilename () string {
@@ -142,7 +98,7 @@ func (rl *RotateLogs) Write(p []byte) (n int, err error) {
14298 rl .mutex .Lock ()
14399 defer rl .mutex .Unlock ()
144100
145- out , err := rl .getTargetWriter ( )
101+ out , err := rl .getWriter_nolock ( false )
146102 if err != nil {
147103 return 0 , errors .Wrap (err , `failed to acquite target io.Writer` )
148104 }
@@ -151,7 +107,7 @@ func (rl *RotateLogs) Write(p []byte) (n int, err error) {
151107}
152108
153109// must be locked during this operation
154- func (rl * RotateLogs ) getTargetWriter ( ) (io.Writer , error ) {
110+ func (rl * RotateLogs ) getWriter_nolock ( bailOnRotateFail bool ) (io.Writer , error ) {
155111 // This filename contains the name of the "NEW" filename
156112 // to log to, which may be newer than rl.currentFilename
157113 filename := rl .genFilename ()
@@ -166,12 +122,16 @@ func (rl *RotateLogs) getTargetWriter() (io.Writer, error) {
166122 return nil , errors .Errorf ("failed to open file %s: %s" , rl .pattern , err )
167123 }
168124
169- if err := rl .rotate (filename ); err != nil {
170- // Failure to rotate is a problem, but it's really not a great
171- // idea to stop your application just because you couldn't rename
172- // your log. For now, we're just going to punt it and write to
173- // os.Stderr
174- fmt .Fprintf (os .Stderr , "failed to rotate: %s\n " , err )
125+ if err := rl .rotate_nolock (filename ); err != nil {
126+ err = errors .Wrap (err , "failed to rotate" )
127+ if bailOnRotateFail {
128+ // Failure to rotate is a problem, but it's really not a great
129+ // idea to stop your application just because you couldn't rename
130+ // your log.
131+ // We only return this error when explicitly needed.
132+ return nil , err
133+ }
134+ fmt .Fprintf (os .Stderr , "%s\n " , err .Error ())
175135 }
176136
177137 rl .outFh .Close ()
@@ -209,7 +169,17 @@ func (g *cleanupGuard) Run() {
209169 g .fn ()
210170}
211171
212- func (rl * RotateLogs ) rotate (filename string ) error {
172+ // Rotate forcefully rotates the log files.
173+ func (rl * RotateLogs ) Rotate () error {
174+ rl .mutex .Lock ()
175+ defer rl .mutex .Unlock ()
176+ if _ , err := rl .getWriter_nolock (true ); err != nil {
177+ return err
178+ }
179+ return nil
180+ }
181+
182+ func (rl * RotateLogs ) rotate_nolock (filename string ) error {
213183 lockfn := filename + `_lock`
214184 fh , err := os .OpenFile (lockfn , os .O_CREATE | os .O_EXCL , 0644 )
215185 if err != nil {
@@ -274,11 +244,11 @@ func (rl *RotateLogs) rotate(filename string) error {
274244
275245 if rl .rotationCount > 0 {
276246 // Only delete if we have more than rotationCount
277- if rl .rotationCount >= len (toUnlink ) {
247+ if rl .rotationCount >= uint ( len (toUnlink ) ) {
278248 return nil
279249 }
280250
281- toUnlink = toUnlink [:len (toUnlink )- rl .rotationCount ]
251+ toUnlink = toUnlink [:len (toUnlink )- int ( rl .rotationCount ) ]
282252 }
283253
284254 if len (toUnlink ) <= 0 {
0 commit comments