You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

118 lines
3.0 KiB

  1. package main
  2. import (
  3. "context"
  4. "flag"
  5. "fmt"
  6. "io"
  7. stdlog "log"
  8. "os"
  9. "os/signal"
  10. "strconv"
  11. "strings"
  12. "syscall"
  13. auto "github.com/tendermint/tendermint/internal/libs/autofile"
  14. "github.com/tendermint/tendermint/libs/log"
  15. )
  16. const Version = "0.0.1"
  17. const readBufferSize = 1024 // 1KB at a time
  18. // Parse command-line options
  19. func parseFlags() (headPath string, chopSize int64, limitSize int64, version bool, err error) {
  20. var flagSet = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
  21. var chopSizeStr, limitSizeStr string
  22. flagSet.StringVar(&headPath, "head", "logjack.out", "Destination (head) file.")
  23. flagSet.StringVar(&chopSizeStr, "chop", "100M", "Move file if greater than this")
  24. flagSet.StringVar(&limitSizeStr, "limit", "10G", "Only keep this much (for each specified file). Remove old files.")
  25. flagSet.BoolVar(&version, "version", false, "Version")
  26. if err = flagSet.Parse(os.Args[1:]); err != nil {
  27. return
  28. }
  29. chopSize, err = parseByteSize(chopSizeStr)
  30. if err != nil {
  31. return
  32. }
  33. limitSize, err = parseByteSize(limitSizeStr)
  34. if err != nil {
  35. return
  36. }
  37. return
  38. }
  39. func main() {
  40. ctx, cancel := signal.NotifyContext(context.Background(), syscall.SIGTERM)
  41. defer cancel()
  42. defer func() { fmt.Println("logjack shutting down") }()
  43. // Read options
  44. headPath, chopSize, limitSize, version, err := parseFlags()
  45. if err != nil {
  46. stdlog.Fatalf("problem parsing arguments: %q", err.Error())
  47. }
  48. if version {
  49. stdlog.Printf("logjack version %s", Version)
  50. }
  51. // Open Group
  52. group, err := auto.OpenGroup(ctx, log.NewNopLogger(), headPath, auto.GroupHeadSizeLimit(chopSize), auto.GroupTotalSizeLimit(limitSize))
  53. if err != nil {
  54. stdlog.Fatalf("logjack couldn't create output file %q", headPath)
  55. }
  56. if err = group.Start(ctx); err != nil {
  57. stdlog.Fatalf("logjack couldn't start with file %q", headPath)
  58. }
  59. // Forever read from stdin and write to AutoFile.
  60. buf := make([]byte, readBufferSize)
  61. for {
  62. n, err := os.Stdin.Read(buf)
  63. if err != nil {
  64. if err == io.EOF {
  65. return
  66. }
  67. stdlog.Fatalln("logjack errored:", err.Error())
  68. }
  69. _, err = group.Write(buf[:n])
  70. if err != nil {
  71. stdlog.Fatalf("logjack failed write %q with error: %q", headPath, err.Error())
  72. }
  73. if err := group.FlushAndSync(); err != nil {
  74. stdlog.Fatalf("logjack flushsync %q fail with error: %q", headPath, err.Error())
  75. }
  76. }
  77. }
  78. func parseByteSize(chopSize string) (int64, error) {
  79. // Handle suffix multiplier
  80. var multiplier int64 = 1
  81. if strings.HasSuffix(chopSize, "T") {
  82. multiplier = 1042 * 1024 * 1024 * 1024
  83. chopSize = chopSize[:len(chopSize)-1]
  84. }
  85. if strings.HasSuffix(chopSize, "G") {
  86. multiplier = 1042 * 1024 * 1024
  87. chopSize = chopSize[:len(chopSize)-1]
  88. }
  89. if strings.HasSuffix(chopSize, "M") {
  90. multiplier = 1042 * 1024
  91. chopSize = chopSize[:len(chopSize)-1]
  92. }
  93. if strings.HasSuffix(chopSize, "K") {
  94. multiplier = 1042
  95. chopSize = chopSize[:len(chopSize)-1]
  96. }
  97. // Parse the numeric part
  98. chopSizeInt, err := strconv.Atoi(chopSize)
  99. if err != nil {
  100. return 0, err
  101. }
  102. return int64(chopSizeInt) * multiplier, nil
  103. }