From 5ce09dca7e5fb033c1e4b6849835d8272b745545 Mon Sep 17 00:00:00 2001 From: Mateusz Szostok Date: Thu, 13 Jul 2023 14:16:43 +0200 Subject: [PATCH] Remove logs scrolling while printing --- cmd/cli/cmd/install.go | 2 - internal/cli/install/config.go | 10 ++- internal/cli/install/install.go | 12 ++-- internal/cli/install/logs/printer.go | 102 ++++++--------------------- internal/cli/printer/status.go | 14 ++-- 5 files changed, 41 insertions(+), 99 deletions(-) diff --git a/cmd/cli/cmd/install.go b/cmd/cli/cmd/install.go index c22399d3be..73e28433bf 100644 --- a/cmd/cli/cmd/install.go +++ b/cmd/cli/cmd/install.go @@ -47,8 +47,6 @@ func NewInstall() *cobra.Command { flags := installCmd.Flags() kubex.RegisterKubeconfigFlag(flags) - flags.BoolVar(&opts.LogsReportTimestamp, "logs-report-timestamp", false, "Print timestamp prefix to the Botkube logs entries") - flags.IntVar(&opts.LogsScrollingHeight, "logs-scrolling-height", 10, "") flags.DurationVar(&opts.Timeout, "timeout", 10*time.Minute, `Maximum time during which the Botkube installation is being watched, where "0" means "infinite". Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".`) flags.BoolVarP(&opts.Watch, "watch", "w", true, "Watches the status of the Botkube installation until it finish or the defined `--timeout` occurs.") diff --git a/internal/cli/install/config.go b/internal/cli/install/config.go index 42176341dd..5bbcf62e40 100644 --- a/internal/cli/install/config.go +++ b/internal/cli/install/config.go @@ -25,10 +25,8 @@ const ( // Config holds parameters for Botkube installation on cluster. type Config struct { - Kubeconfig string - HelmParams helm.Config - LogsReportTimestamp bool - LogsScrollingHeight int - Watch bool - Timeout time.Duration + Kubeconfig string + HelmParams helm.Config + Watch bool + Timeout time.Duration } diff --git a/internal/cli/install/install.go b/internal/cli/install/install.go index 4e996e9e2e..3635efe300 100644 --- a/internal/cli/install/install.go +++ b/internal/cli/install/install.go @@ -109,7 +109,7 @@ func Install(ctx context.Context, w io.Writer, k8sCfg *kubex.ConfigWithMeta, opt return fmt.Errorf("Timed out waiting for Pod") } - messages := make(chan []byte, opts.LogsScrollingHeight) + messages := make(chan []byte, 100) streamLogCtx, cancelStreamLogs := context.WithCancel(context.Background()) defer cancelStreamLogs() parallel.Go(func() error { @@ -117,13 +117,12 @@ func Install(ctx context.Context, w io.Writer, k8sCfg *kubex.ConfigWithMeta, opt return logs.StartsLogsStreaming(streamLogCtx, clientset, opts.HelmParams.Namespace, podName, messages) }) - logsPrinter := logs.NewFixedHeightPrinter( - opts.LogsScrollingHeight, + logsPrinter := logs.NewPrinter( podName, ) parallel.Go(func() error { - logsPrinter.Start(ctxWithTimeout) + logsPrinter.Start(ctxWithTimeout, status) return nil }) parallel.Go(func() error { @@ -158,7 +157,10 @@ func Install(ctx context.Context, w io.Writer, k8sCfg *kubex.ConfigWithMeta, opt err = parallel.Wait() if err != nil { - printFailedInstallMessage(opts.HelmParams.Version, opts.HelmParams.Namespace, podName, w) + err := printFailedInstallMessage(opts.HelmParams.Version, opts.HelmParams.Namespace, podName, w) + if err != nil { + return err + } return err } diff --git a/internal/cli/install/logs/printer.go b/internal/cli/install/logs/printer.go index 0cd22b5917..5bf33a2a09 100644 --- a/internal/cli/install/logs/printer.go +++ b/internal/cli/install/logs/printer.go @@ -1,26 +1,21 @@ package logs import ( - "bytes" "context" "fmt" "os" "strings" - "time" "github.com/charmbracelet/log" charmlog "github.com/charmbracelet/log" "github.com/morikuni/aec" - "github.com/muesli/termenv" + "github.com/muesli/reflow/indent" "github.com/kubeshop/botkube/internal/cli" "github.com/kubeshop/botkube/internal/cli/printer" ) -type FixedHeightPrinter struct { - height int - logsBuffer []string - podPhase string +type Printer struct { podName string newLog chan string newPodPhase chan string @@ -29,115 +24,64 @@ type FixedHeightPrinter struct { logger *log.Logger } -func NewFixedHeightPrinter(height int, name string) *FixedHeightPrinter { - return &FixedHeightPrinter{ - height: height, - logsBuffer: []string{}, +func NewPrinter(podName string) *Printer { + return &Printer{ newLog: make(chan string, 10), newPodPhase: make(chan string, 10), stop: make(chan struct{}), logger: log.NewWithOptions(os.Stdout, log.Options{ Formatter: log.TextFormatter, }), - podName: name, + podName: podName, parser: JSONParser{}, } } -func (f *FixedHeightPrinter) Start(ctx context.Context) { - refreshDuration := 100 * time.Millisecond - idleDelay := time.NewTimer(refreshDuration) - defer idleDelay.Stop() +func (f *Printer) Start(ctx context.Context, status *printer.StatusPrinter) { + status.InfoWithBody("Streaming logs...", indent.String(fmt.Sprintf("Pod: %s", f.podName), 4)) + fmt.Println() - buff := bytes.Buffer{} - status := printer.NewStatus(&buff, "") - status.Step("Streaming logs...") - termenv.SaveCursorPosition() for { - f.printData(buff.String() + "\n") // it's without new line when it's in progress - idleDelay.Reset(refreshDuration) - select { case <-f.stop: - status.End(true) - termenv.RestoreCursorPosition() - f.printStatusHeader(buff.String()) - f.printLogs(true) - fmt.Println() return case <-ctx.Done(): - status.End(false) - termenv.RestoreCursorPosition() - - f.printStatusHeader(buff.String()) - f.printLogs(true) - fmt.Println() + status.Infof("Requested logs streaming cancel...") return - case <-idleDelay.C: case entry := <-f.newLog: - f.logsBuffer = append(f.logsBuffer, entry) - if len(f.logsBuffer) > f.height { - //now we need to simulate scrolling, so all lines are moved N-1, where the first line is just removed. - f.logsBuffer = f.logsBuffer[1:] - } - case podPhase := <-f.newPodPhase: - f.podPhase = podPhase + f.printLogs(entry) } - fmt.Print(aec.Up(uint(f.height + 4))) } } -func (f *FixedHeightPrinter) printData(header string) { - f.printStatusHeader(header) - f.printLogs(false) -} -func (f *FixedHeightPrinter) printStatusHeader(step string) { - fmt.Println(step) - fmt.Printf(" Pods: %s Phase: %s\n", f.podName, f.podPhase) - fmt.Println() -} - -func (f *FixedHeightPrinter) UpdatePodPhase(phase string) { +func (f *Printer) UpdatePodPhase(phase string) { select { case f.newPodPhase <- phase: default: } } -func (f *FixedHeightPrinter) printLogs(skip bool) { - wroteLines := 0 - for _, item := range f.logsBuffer { - fields, lvl := f.parser.ParseLineIntoCharm(item) - if fields == nil { - wroteLines++ - f.printLogLine(item) - continue - } - if lvl == charmlog.DebugLevel && !cli.VerboseMode.IsEnabled() { - continue - } - wroteLines++ - fmt.Print(aec.EraseLine(aec.EraseModes.Tail)) - fmt.Print(aec.Column(6)) - f.logger.With(fields...).Print(nil) - } - - if skip { +func (f *Printer) printLogs(item string) { + fields, lvl := f.parser.ParseLineIntoCharm(item) + if fields == nil { + f.printLogLine(item) return } - for i := wroteLines; i < f.height; i++ { - f.printLogLine("\n") + if lvl == charmlog.DebugLevel && !cli.VerboseMode.IsEnabled() { + return } + fmt.Print(aec.EraseLine(aec.EraseModes.Tail)) + fmt.Print(aec.Column(6)) + f.logger.With(fields...).Print(nil) } -func (f *FixedHeightPrinter) printLogLine(line string) { - fmt.Print(aec.EraseLine(aec.EraseModes.Tail)) +func (f *Printer) printLogLine(line string) { fmt.Print(aec.Column(6)) fmt.Print(line) } -func (f *FixedHeightPrinter) AppendLogEntry(entry string) { +func (f *Printer) AppendLogEntry(entry string) { if strings.TrimSpace(entry) == "" { return } @@ -147,6 +91,6 @@ func (f *FixedHeightPrinter) AppendLogEntry(entry string) { } } -func (f *FixedHeightPrinter) Stop() { +func (f *Printer) Stop() { close(f.stop) } diff --git a/internal/cli/printer/status.go b/internal/cli/printer/status.go index 6b1ee30833..be4cb5970b 100644 --- a/internal/cli/printer/status.go +++ b/internal/cli/printer/status.go @@ -50,13 +50,13 @@ func NewStatus(w io.Writer, header string) *StatusPrinter { st := &StatusPrinter{ w: w, } - //if cli.IsSmartTerminal(w) { - st.durationSprintf = color.New(color.Faint, color.Italic).Sprintf - st.spinner = NewDynamicSpinner(w) - //} else { - // st.durationSprintf = fmt.Sprintf - // st.spinner = NewStaticSpinner(w) - //} + if cli.IsSmartTerminal(w) { + st.durationSprintf = color.New(color.Faint, color.Italic).Sprintf + st.spinner = NewDynamicSpinner(w) + } else { + st.durationSprintf = fmt.Sprintf + st.spinner = NewStaticSpinner(w) + } return st }