-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrotate.go
104 lines (91 loc) · 2.82 KB
/
rotate.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
package logger
import (
"context"
"fmt"
"os"
"path/filepath"
"time"
)
// File naming variables
var (
name string
extension string
)
// generateLogFileName creates a unique log filename using timestamp with increasing precision.
// It ensures uniqueness by progressively adding more precise subsecond components.
func generateLogFileName(baseName string, timestamp time.Time) (string, error) {
baseTimestamp := timestamp.Format("060102_150405")
// Always include first decimal place (tenth of a second)
tenths := (timestamp.UnixNano() % 1e9) / 1e8
filename := fmt.Sprintf("%s_%s_%d.%s", baseName, baseTimestamp, tenths, extension)
fullPath := filepath.Join(directory, filename)
if _, err := os.Stat(fullPath); os.IsNotExist(err) {
return filename, nil
}
// If file exists, try additional precision
for precision := 2; precision <= 9; precision++ {
subseconds := timestamp.UnixNano() % 1e9 / pow10(9-precision)
subsecFormat := fmt.Sprintf("%%0%dd", precision)
filename = fmt.Sprintf("%s_%s_%s.%s",
baseName, baseTimestamp, fmt.Sprintf(subsecFormat, subseconds), extension)
fullPath = filepath.Join(directory, filename)
if _, err := os.Stat(fullPath); os.IsNotExist(err) {
return filename, nil
}
}
return "", fmt.Errorf("failed to generate unique log filename")
}
// pow10 calculates powers of 10 for subsecond precision in log filenames.
// It's used by generateLogFileName to create unique timestamp-based names.
func pow10(n int) int64 {
result := int64(1)
for i := 0; i < n; i++ {
result *= 10
}
return result
}
// createNewLogFile generates and opens a new log file with proper permissions.
// It ensures unique naming and proper file creation with append mode.
func createNewLogFile(ctx context.Context) (*os.File, error) {
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
filename, err := generateLogFileName(name, time.Now())
if err != nil {
return nil, fmt.Errorf("failed to generate log filename: %w", err)
}
file, err := os.OpenFile(
filepath.Join(directory, filename),
os.O_APPEND|os.O_CREATE|os.O_WRONLY,
0644,
)
if err != nil {
return nil, fmt.Errorf("failed to create log file: %w", err)
}
return file, nil
}
}
// rotateLogFile handles the log rotation process, creating new file and closing old one.
// It updates all necessary state while maintaining thread safety.
func rotateLogFile(ctx context.Context) error {
select {
case <-ctx.Done():
return ctx.Err()
default:
newFile, err := createNewLogFile(ctx)
if err != nil {
return fmt.Errorf("failed to create new log file: %w", err)
}
oldFile := currentFile.Load().(*os.File)
if oldFile != nil {
if err := oldFile.Close(); err != nil {
newFile.Close()
return fmt.Errorf("failed to close old log file: %w", err)
}
}
currentFile.Store(newFile)
currentSize.Store(0)
return nil
}
}