file/trigger.go (136 lines of code) (raw):

// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you under // the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package file import ( "time" ) // rotateReason is the reason why file rotation occurred. type rotateReason uint32 const ( rotateReasonNoRotate rotateReason = iota rotateReasonInitializing rotateReasonFileSize rotateReasonManualTrigger rotateReasonTimeInterval ) func (rr rotateReason) String() string { switch rr { case rotateReasonInitializing: return "initializing" case rotateReasonFileSize: return "file size" case rotateReasonManualTrigger: return "manual trigger" case rotateReasonTimeInterval: return "time interval" default: return "unknown" } } // trigger interface causes the log writer to rotate the active file. type trigger interface { TriggerRotation(dataLen uint) rotateReason } func newTriggers(rotateOnStartup bool, interval time.Duration, maxSizeBytes uint, clock clock) []trigger { triggers := make([]trigger, 0) if rotateOnStartup { triggers = append(triggers, &initTrigger{}) } if interval > 0 { triggers = append(triggers, newIntervalTrigger(interval, clock)) } if maxSizeBytes > 0 { triggers = append(triggers, &sizeTrigger{maxSizeBytes: maxSizeBytes, size: 0}) } return triggers } // initTrigger is triggered once on startup. type initTrigger struct { triggered bool } func (t *initTrigger) TriggerRotation(_ uint) rotateReason { if !t.triggered { t.triggered = true return rotateReasonInitializing } return rotateReasonNoRotate } // sizeTrigger starts a rotation when the file reaches the configured size. type sizeTrigger struct { maxSizeBytes uint size uint } func (t *sizeTrigger) TriggerRotation(dataLen uint) rotateReason { if t.size+dataLen > t.maxSizeBytes { t.size = 0 return rotateReasonFileSize } t.size += dataLen return rotateReasonNoRotate } // intervalTrigger rotates the files after the configured interval. type intervalTrigger struct { interval time.Duration clock clock lastRotate time.Time newInterval func(lastTime time.Time, currentTime time.Time) bool } type clock interface { Now() time.Time } type realClock struct{} func (realClock) Now() time.Time { return time.Now() } func newIntervalTrigger(interval time.Duration, clock clock) trigger { t := intervalTrigger{interval: interval, clock: clock} switch interval { case time.Second: t.newInterval = newSecond case time.Minute: t.newInterval = newMinute case time.Hour: t.newInterval = newHour case 24 * time.Hour: // calendar day t.newInterval = newDay case 7 * 24 * time.Hour: // calendar week t.newInterval = newWeek case 30 * 24 * time.Hour: // calendar month t.newInterval = newMonth case 365 * 24 * time.Hour: // calendar year t.newInterval = newYear default: t.newInterval = func(lastTime time.Time, currentTime time.Time) bool { lastInterval := lastTime.Unix() / (int64(t.interval) / int64(time.Second)) currentInterval := currentTime.Unix() / (int64(t.interval) / int64(time.Second)) return lastInterval != currentInterval } } t.lastRotate = clock.Now() return &t } func (t *intervalTrigger) TriggerRotation(_ uint) rotateReason { now := t.clock.Now() if t.newInterval(t.lastRotate, now) { t.lastRotate = now return rotateReasonTimeInterval } return rotateReasonNoRotate } func newSecond(lastTime time.Time, currentTime time.Time) bool { return lastTime.Second() != currentTime.Second() || newMinute(lastTime, currentTime) } func newMinute(lastTime time.Time, currentTime time.Time) bool { return lastTime.Minute() != currentTime.Minute() || newHour(lastTime, currentTime) } func newHour(lastTime time.Time, currentTime time.Time) bool { return lastTime.Hour() != currentTime.Hour() || newDay(lastTime, currentTime) } func newDay(lastTime time.Time, currentTime time.Time) bool { return lastTime.Day() != currentTime.Day() || newMonth(lastTime, currentTime) } func newWeek(lastTime time.Time, currentTime time.Time) bool { lastYear, lastWeek := lastTime.ISOWeek() currentYear, currentWeek := currentTime.ISOWeek() return lastWeek != currentWeek || lastYear != currentYear } func newMonth(lastTime time.Time, currentTime time.Time) bool { return lastTime.Month() != currentTime.Month() || newYear(lastTime, currentTime) } func newYear(lastTime time.Time, currentTime time.Time) bool { return lastTime.Year() != currentTime.Year() }