package main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
. "github.com/tendermint/tendermint/common"
|
|
)
|
|
|
|
const Version = "0.0.1"
|
|
const sleepSeconds = 1 // Every second
|
|
|
|
// Parse command-line options
|
|
func parseFlags() (chopSize int64, limitSize int64, version bool, logFiles []string) {
|
|
var chopSizeStr, limitSizeStr string
|
|
flag.StringVar(&chopSizeStr, "chopSize", "1M", "Move file if greater than this")
|
|
flag.StringVar(&limitSizeStr, "limitSize", "1G", "Only keep this much (for each specified file). Remove old files.")
|
|
flag.BoolVar(&version, "version", false, "Version")
|
|
flag.Parse()
|
|
logFiles = flag.Args()
|
|
chopSize = parseBytesize(chopSizeStr)
|
|
limitSize = parseBytesize(limitSizeStr)
|
|
return
|
|
}
|
|
|
|
func main() {
|
|
|
|
// Read options
|
|
chopSize, limitSize, version, logFiles := parseFlags()
|
|
if version {
|
|
fmt.Println(Fmt("logjack version %v", Version))
|
|
return
|
|
}
|
|
|
|
// Print args.
|
|
// fmt.Println(chopSize, limitSiz,e version, logFiles)
|
|
|
|
go func() {
|
|
for {
|
|
for _, logFilePath := range logFiles {
|
|
minIndex, maxIndex, totalSize, baseSize := readLogInfo(logFilePath)
|
|
if chopSize < baseSize {
|
|
moveLog(logFilePath, Fmt("%v.%03d", logFilePath, maxIndex+1))
|
|
}
|
|
if limitSize < totalSize {
|
|
// NOTE: we only remove one file at a time.
|
|
removeLog(Fmt("%v.%03d", logFilePath, minIndex))
|
|
}
|
|
}
|
|
time.Sleep(sleepSeconds * time.Second)
|
|
}
|
|
}()
|
|
|
|
// Trap signal
|
|
TrapSignal(func() {
|
|
fmt.Println("logjack shutting down")
|
|
})
|
|
}
|
|
|
|
func moveLog(oldPath, newPath string) {
|
|
err := os.Rename(oldPath, newPath)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
func removeLog(path string) {
|
|
err := os.Remove(path)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
}
|
|
|
|
// This is a strange function. Refactor everything to make it less strange?
|
|
func readLogInfo(logPath string) (minIndex, maxIndex int, totalSize int64, baseSize int64) {
|
|
|
|
logDir := filepath.Dir(logPath)
|
|
logFile := filepath.Base(logPath)
|
|
minIndex, maxIndex = -1, -1
|
|
|
|
dir, err := os.Open(logDir)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
fi, err := dir.Readdir(0)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
for _, fileInfo := range fi {
|
|
indexedFilePattern := regexp.MustCompile("^.+\\.([0-9]{3,})$")
|
|
if fileInfo.Name() == logFile {
|
|
baseSize = fileInfo.Size()
|
|
continue
|
|
} else if strings.HasPrefix(fileInfo.Name(), logFile) {
|
|
totalSize += fileInfo.Size()
|
|
submatch := indexedFilePattern.FindSubmatch([]byte(fileInfo.Name()))
|
|
if len(submatch) != 0 {
|
|
// Matches
|
|
logIndex, err := strconv.Atoi(string(submatch[1]))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
if maxIndex < logIndex {
|
|
maxIndex = logIndex
|
|
}
|
|
if minIndex == -1 || logIndex < minIndex {
|
|
minIndex = logIndex
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return minIndex, maxIndex, totalSize, baseSize
|
|
}
|
|
|
|
func parseBytesize(chopSize string) int64 {
|
|
// Handle suffix multiplier
|
|
var multiplier int64 = 1
|
|
if strings.HasSuffix(chopSize, "T") {
|
|
multiplier = 1042 * 1024 * 1024 * 1024
|
|
chopSize = chopSize[:len(chopSize)-1]
|
|
}
|
|
if strings.HasSuffix(chopSize, "G") {
|
|
multiplier = 1042 * 1024 * 1024
|
|
chopSize = chopSize[:len(chopSize)-1]
|
|
}
|
|
if strings.HasSuffix(chopSize, "M") {
|
|
multiplier = 1042 * 1024
|
|
chopSize = chopSize[:len(chopSize)-1]
|
|
}
|
|
if strings.HasSuffix(chopSize, "K") {
|
|
multiplier = 1042
|
|
chopSize = chopSize[:len(chopSize)-1]
|
|
}
|
|
|
|
// Parse the numeric part
|
|
chopSizeInt, err := strconv.Atoi(chopSize)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
return int64(chopSizeInt) * multiplier
|
|
}
|