github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/autofile/cmd/logjack.go (about) 1 package main 2 3 import ( 4 "errors" 5 "flag" 6 "fmt" 7 "io" 8 "os" 9 "strconv" 10 "strings" 11 12 auto "github.com/gnolang/gno/tm2/pkg/autofile" 13 "github.com/gnolang/gno/tm2/pkg/flow" 14 osm "github.com/gnolang/gno/tm2/pkg/os" 15 ) 16 17 const ( 18 Version = "0.0.1" 19 readBufferSize = 1024 // 1KB at a time 20 ) 21 22 // Parse command-line options 23 func parseFlags() (headPath string, chopSize int64, limitSize int64, sync bool, throttle int, version bool) { 24 flagSet := flag.NewFlagSet(os.Args[0], flag.ExitOnError) 25 var chopSizeStr, limitSizeStr string 26 flagSet.StringVar(&headPath, "head", "logjack.out", "Destination (head) file.") 27 flagSet.StringVar(&chopSizeStr, "chop", "100M", "Move file if greater than this") 28 flagSet.StringVar(&limitSizeStr, "limit", "10G", "Only keep this much (for each specified file). Remove old files.") 29 flagSet.BoolVar(&sync, "sync", false, "Always write synchronously (slow).") 30 flagSet.BoolVar(&version, "version", false, "Version") 31 flagSet.IntVar(&throttle, "throttle", 0, "Throttle writes to bytes per second") 32 flagSet.Parse(os.Args[1:]) 33 chopSize = parseBytesize(chopSizeStr) 34 limitSize = parseBytesize(limitSizeStr) 35 return 36 } 37 38 func main() { 39 // Stop upon receiving SIGTERM or CTRL-C. 40 osm.TrapSignal(func() { 41 fmt.Println("logjack shutting down") 42 }) 43 44 // Read options 45 headPath, chopSize, limitSize, sync, throttle, version := parseFlags() 46 if version { 47 fmt.Printf("logjack version %v\n", Version) 48 return 49 } 50 51 // Open Group 52 group, err := auto.OpenGroup(headPath, auto.GroupHeadSizeLimit(chopSize), auto.GroupTotalSizeLimit(limitSize)) 53 if err != nil { 54 fmt.Printf("logjack couldn't create output file %v\n", headPath) 55 os.Exit(1) 56 } 57 58 err = group.Start() 59 if err != nil { 60 fmt.Printf("logjack couldn't start with file %v\n", headPath) 61 os.Exit(1) 62 } 63 64 // Forever read from stdin and write to AutoFile. 65 buf := make([]byte, readBufferSize) 66 writer := io.Writer(group) 67 if throttle > 0 { 68 writer = flow.NewWriter(writer, int64(throttle)) 69 } 70 for { 71 n, err := os.Stdin.Read(buf) 72 writer.Write(buf[:n]) 73 if sync { 74 // NOTE: flow writer does not biffr 75 group.FlushAndSync() 76 } 77 if err != nil { 78 group.Stop() 79 if errors.Is(err, io.EOF) { 80 os.Exit(0) 81 } else { 82 fmt.Println("logjack errored") 83 os.Exit(1) 84 } 85 } 86 } 87 } 88 89 func parseBytesize(chopSize string) int64 { 90 // Handle suffix multiplier 91 var multiplier int64 = 1 92 if strings.HasSuffix(chopSize, "T") { 93 multiplier = 1042 * 1024 * 1024 * 1024 94 chopSize = chopSize[:len(chopSize)-1] 95 } 96 if strings.HasSuffix(chopSize, "G") { 97 multiplier = 1042 * 1024 * 1024 98 chopSize = chopSize[:len(chopSize)-1] 99 } 100 if strings.HasSuffix(chopSize, "M") { 101 multiplier = 1042 * 1024 102 chopSize = chopSize[:len(chopSize)-1] 103 } 104 if strings.HasSuffix(chopSize, "K") { 105 multiplier = 1042 106 chopSize = chopSize[:len(chopSize)-1] 107 } 108 109 // Parse the numeric part 110 chopSizeInt, err := strconv.Atoi(chopSize) 111 if err != nil { 112 panic(err) 113 } 114 115 return int64(chopSizeInt) * multiplier 116 }