starlog/rotate_templates.go

218 lines
6.1 KiB
Go
Raw Normal View History

2026-03-19 16:37:57 +08:00
package starlog
import (
"errors"
"os"
"path/filepath"
"strings"
"sync"
"time"
)
const defaultRotateNamePattern = "20060102-150405"
func normalizeRotateNamePattern(pattern string) string {
pattern = strings.TrimSpace(pattern)
if pattern == "" {
return defaultRotateNamePattern
}
return pattern
}
func buildRotatePathWithPattern(current string, now time.Time, pattern string) string {
pattern = normalizeRotateNamePattern(pattern)
dir := filepath.Dir(current)
base := filepath.Base(current)
ext := filepath.Ext(base)
stem := strings.TrimSuffix(base, ext)
suffix := now.Format(pattern)
if ext == "" {
return filepath.Join(dir, stem+"."+suffix)
}
return filepath.Join(dir, stem+"."+suffix+ext)
}
func resolveEntryTime(entry *Entry) time.Time {
if entry != nil && !entry.Time.IsZero() {
return entry.Time
}
return time.Now()
}
type RotateByTimePolicy struct {
interval time.Duration
pattern string
mu sync.Mutex
lastRotate time.Time
}
func NewRotateByTimePolicy(interval time.Duration) *RotateByTimePolicy {
return NewRotateByTimePolicyWithPattern(interval, "")
}
func NewRotateByTimePolicyWithPattern(interval time.Duration, pattern string) *RotateByTimePolicy {
if interval <= 0 {
interval = 24 * time.Hour
}
return &RotateByTimePolicy{
interval: interval,
pattern: normalizeRotateNamePattern(pattern),
}
}
func (policy *RotateByTimePolicy) ShouldRotate(info FileInfo, entry *Entry) bool {
if policy == nil || info == nil {
return false
}
now := resolveEntryTime(entry)
policy.mu.Lock()
defer policy.mu.Unlock()
if policy.lastRotate.IsZero() {
policy.lastRotate = GetFileCreationTime(info)
if policy.lastRotate.IsZero() {
policy.lastRotate = now
}
return false
}
if now.Sub(policy.lastRotate) >= policy.interval {
policy.lastRotate = now
return true
}
return false
}
func (policy *RotateByTimePolicy) NextPath(current string, now time.Time) string {
if policy == nil {
return buildRotatePathWithPattern(current, now, "")
}
return buildRotatePathWithPattern(current, now, policy.pattern)
}
type RotateBySizePolicy struct {
maxSize int64
pattern string
}
func NewRotateBySizePolicy(maxSizeBytes int64) *RotateBySizePolicy {
return NewRotateBySizePolicyWithPattern(maxSizeBytes, "")
}
func NewRotateBySizePolicyWithPattern(maxSizeBytes int64, pattern string) *RotateBySizePolicy {
if maxSizeBytes <= 0 {
maxSizeBytes = 100 * 1024 * 1024
}
return &RotateBySizePolicy{
maxSize: maxSizeBytes,
pattern: normalizeRotateNamePattern(pattern),
}
}
func (policy *RotateBySizePolicy) ShouldRotate(info FileInfo, entry *Entry) bool {
_ = entry
if policy == nil || info == nil {
return false
}
return info.Size() >= policy.maxSize
}
func (policy *RotateBySizePolicy) NextPath(current string, now time.Time) string {
if policy == nil {
return buildRotatePathWithPattern(current, now, "")
}
return buildRotatePathWithPattern(current, now, policy.pattern)
}
type RotateByTimeSizePolicy struct {
interval time.Duration
maxSize int64
pattern string
mu sync.Mutex
lastRotate time.Time
}
func NewRotateByTimeSizePolicy(interval time.Duration, maxSizeBytes int64) *RotateByTimeSizePolicy {
return NewRotateByTimeSizePolicyWithPattern(interval, maxSizeBytes, "")
}
func NewRotateByTimeSizePolicyWithPattern(interval time.Duration, maxSizeBytes int64, pattern string) *RotateByTimeSizePolicy {
if interval <= 0 {
interval = 24 * time.Hour
}
if maxSizeBytes <= 0 {
maxSizeBytes = 100 * 1024 * 1024
}
return &RotateByTimeSizePolicy{
interval: interval,
maxSize: maxSizeBytes,
pattern: normalizeRotateNamePattern(pattern),
}
}
func (policy *RotateByTimeSizePolicy) ShouldRotate(info FileInfo, entry *Entry) bool {
if policy == nil || info == nil {
return false
}
now := resolveEntryTime(entry)
policy.mu.Lock()
defer policy.mu.Unlock()
if policy.lastRotate.IsZero() {
policy.lastRotate = GetFileCreationTime(info)
if policy.lastRotate.IsZero() {
policy.lastRotate = now
}
}
if info.Size() >= policy.maxSize {
policy.lastRotate = now
return true
}
if now.Sub(policy.lastRotate) >= policy.interval {
policy.lastRotate = now
return true
}
return false
}
func (policy *RotateByTimeSizePolicy) NextPath(current string, now time.Time) string {
if policy == nil {
return buildRotatePathWithPattern(current, now, "")
}
return buildRotatePathWithPattern(current, now, policy.pattern)
}
func StartRotateByTime(logger *StarLogger, interval time.Duration, checkInterval int64) error {
return StartRotatePolicy(logger, NewRotateByTimePolicy(interval), checkInterval)
}
func StartRotateBySize(logger *StarLogger, maxSizeBytes int64, checkInterval int64) error {
return StartRotatePolicy(logger, NewRotateBySizePolicy(maxSizeBytes), checkInterval)
}
func StartRotateByTimeSize(logger *StarLogger, interval time.Duration, maxSizeBytes int64, checkInterval int64) error {
return StartRotatePolicy(logger, NewRotateByTimeSizePolicy(interval, maxSizeBytes), checkInterval)
}
func StartManagedRotatePolicy(logger *StarLogger, policy RotatePolicy, checkInterval int64, options RotateManageOptions) error {
if policy == nil {
return errors.New("rotate policy is nil")
}
strategy := buildRotateStrategy(policy, checkInterval)
strategy.afterHook = func(logger *StarLogger, archivePath string, currentPath string, info os.FileInfo) error {
return ApplyRotateManageOptions(archivePath, currentPath, options)
}
return startArchiveWithStrategy(logger, strategy)
}
func StartManagedRotateByTime(logger *StarLogger, interval time.Duration, checkInterval int64, options RotateManageOptions) error {
return StartManagedRotatePolicy(logger, NewRotateByTimePolicy(interval), checkInterval, options)
}
func StartManagedRotateBySize(logger *StarLogger, maxSizeBytes int64, checkInterval int64, options RotateManageOptions) error {
return StartManagedRotatePolicy(logger, NewRotateBySizePolicy(maxSizeBytes), checkInterval, options)
}
func StartManagedRotateByTimeSize(logger *StarLogger, interval time.Duration, maxSizeBytes int64, checkInterval int64, options RotateManageOptions) error {
return StartManagedRotatePolicy(logger, NewRotateByTimeSizePolicy(interval, maxSizeBytes), checkInterval, options)
}