forked from mailgun/glogutils
/
glogutils.go
130 lines (114 loc) · 3.45 KB
/
glogutils.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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package glogutils
import (
"flag"
"fmt"
"os"
"path/filepath"
"strconv"
"strings"
"time"
"github.com/golang/glog"
)
const DEFAULT_LOGMAXAGE = 0 // default max age in days
var logMaxAge = flag.Int("log_max_age", DEFAULT_LOGMAXAGE,
"If non-empty, it determines the maximum # of days (exclusive) to retain old log files.")
// Removes old logs generated by golang, should be called in some separate goroutine
func CleanupLogs() error {
currentDir := LogDir()
if currentDir == "" {
glog.Infof("Glog's log dir is not set, nothing to clean up")
return nil
}
logMaxAge := LogMaxAge()
glog.Infof("Will clean up logs olderthan maxage [%d days] in: %s", logMaxAge, currentDir)
return removeFiles(currentDir, programName(), logMaxAge)
}
// Shortcut to check if the path is actually a dir
func isDir(path string) (bool, error) {
fi, err := os.Lstat(path)
if err != nil {
return false, err
}
return fi.IsDir(), nil
}
// Determines current program name as it's being used
// by golang to generate log file names
func programName() string {
return filepath.Base(os.Args[0])
}
// Determines whether golang's log_dir has been set without overriding it
func LogDir() string {
logDir := ""
if f := flag.Lookup("log_dir"); f != nil {
logDir = f.Value.String()
}
return logDir
}
// Determines whether log_max_age has been set without overriding it.
// LogMaxAge is the maximum # of days (exclusive) to retain old files based on the timestamp encoded in their filename.
// log_max_age <= 0 means delete immediately
func LogMaxAge() int {
if f := flag.Lookup("log_max_age"); f != nil {
if v, err := strconv.Atoi(f.Value.String()); err == nil {
*logMaxAge = v
}
}
return *logMaxAge
}
// Function that removes files in the logDir matching prefix
// It does not touch symlinks and the files that are being referenced
// by symlinks, it also skips directories
// If logMaxAge > 0, it retains the files that are created within logMaxAge days.
func removeFiles(logDir string, prefix string, maxAge int) error {
pattern := fmt.Sprintf("%s/%s*", logDir, prefix)
files, err := filepath.Glob(pattern)
if err != nil {
glog.Errorf("Failed to glob %s %s", pattern, err)
return err
} else {
glog.Infof("Found files matching %s: %v", pattern, files)
}
toSkip := make(map[string]bool)
timeToRetain := time.Now().Add(-time.Duration(int64(24*time.Hour) * int64(maxAge)))
for _, file := range files {
dir, err := isDir(file)
if err != nil {
return err
}
if dir {
toSkip[file] = true
} else {
realFile, err := filepath.EvalSymlinks(file)
if err != nil {
glog.Errorf("Failed to eval symlink %s", file, err)
return err
}
if realFile != file {
toSkip[file] = true
toSkip[realFile] = true
} else if maxAge > 0 {
// extract the date from filename ('%s.%s.%s.log.%s.%04d%02d%02d-%02d%02d%02d.%d')
// format refers to logName func in github.com/golang/glog/blob/master/glog_file.go
if logName := strings.Split(realFile, "."); len(logName) > 2 /* avoid panic */ {
d, err := time.ParseInLocation("20060102", strings.SplitN(logName[len(logName)-2], "-", 2)[0], time.Local)
if err == nil && d.After(timeToRetain) {
toSkip[realFile] = true
}
}
}
}
}
for _, file := range files {
if toSkip[file] {
glog.Infof("Skipping file: %s", file)
} else {
glog.Infof("Removing: %s", file)
err = os.Remove(file)
if err != nil {
glog.Errorf("Failed to remove %s: %s", file, err)
return err
}
}
}
return nil
}