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  }