package main
|
|
|
|
import (
|
|
"context"
|
|
"flag"
|
|
"fmt"
|
|
"io"
|
|
stdlog "log"
|
|
"os"
|
|
"os/signal"
|
|
"strconv"
|
|
"strings"
|
|
"syscall"
|
|
|
|
auto "github.com/tendermint/tendermint/internal/libs/autofile"
|
|
"github.com/tendermint/tendermint/libs/log"
|
|
)
|
|
|
|
const Version = "0.0.1"
|
|
const readBufferSize = 1024 // 1KB at a time
|
|
|
|
// Parse command-line options
|
|
func parseFlags() (headPath string, chopSize int64, limitSize int64, version bool, err error) {
|
|
var flagSet = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
|
|
var chopSizeStr, limitSizeStr string
|
|
flagSet.StringVar(&headPath, "head", "logjack.out", "Destination (head) file.")
|
|
flagSet.StringVar(&chopSizeStr, "chop", "100M", "Move file if greater than this")
|
|
flagSet.StringVar(&limitSizeStr, "limit", "10G", "Only keep this much (for each specified file). Remove old files.")
|
|
flagSet.BoolVar(&version, "version", false, "Version")
|
|
|
|
if err = flagSet.Parse(os.Args[1:]); err != nil {
|
|
return
|
|
}
|
|
|
|
chopSize, err = parseByteSize(chopSizeStr)
|
|
if err != nil {
|
|
return
|
|
}
|
|
limitSize, err = parseByteSize(limitSizeStr)
|
|
if err != nil {
|
|
return
|
|
}
|
|
return
|
|
}
|
|
|
|
func main() {
|
|
ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGTERM)
|
|
defer cancel()
|
|
defer func() { fmt.Println("logjack shutting down") }()
|
|
|
|
// Read options
|
|
headPath, chopSize, limitSize, version, err := parseFlags()
|
|
if err != nil {
|
|
stdlog.Fatalf("problem parsing arguments: %q", err.Error())
|
|
}
|
|
|
|
if version {
|
|
stdlog.Printf("logjack version %s", Version)
|
|
}
|
|
|
|
// Open Group
|
|
group, err := auto.OpenGroup(ctx, log.NewNopLogger(), headPath, auto.GroupHeadSizeLimit(chopSize), auto.GroupTotalSizeLimit(limitSize))
|
|
if err != nil {
|
|
stdlog.Fatalf("logjack couldn't create output file %q", headPath)
|
|
}
|
|
|
|
if err = group.Start(ctx); err != nil {
|
|
stdlog.Fatalf("logjack couldn't start with file %q", headPath)
|
|
}
|
|
|
|
// Forever read from stdin and write to AutoFile.
|
|
buf := make([]byte, readBufferSize)
|
|
for {
|
|
n, err := os.Stdin.Read(buf)
|
|
if err != nil {
|
|
if err == io.EOF {
|
|
return
|
|
}
|
|
stdlog.Fatalln("logjack errored:", err.Error())
|
|
}
|
|
_, err = group.Write(buf[:n])
|
|
if err != nil {
|
|
stdlog.Fatalf("logjack failed write %q with error: %q", headPath, err.Error())
|
|
}
|
|
if err := group.FlushAndSync(); err != nil {
|
|
stdlog.Fatalf("logjack flushsync %q fail with error: %q", headPath, err.Error())
|
|
}
|
|
}
|
|
}
|
|
|
|
func parseByteSize(chopSize string) (int64, error) {
|
|
// 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 {
|
|
return 0, err
|
|
}
|
|
|
|
return int64(chopSizeInt) * multiplier, nil
|
|
}
|