diff --git a/cmd/formatter/logs.go b/cmd/formatter/logs.go index a5abf028e..db8fa885c 100644 --- a/cmd/formatter/logs.go +++ b/cmd/formatter/logs.go @@ -118,10 +118,6 @@ func (l *logConsumer) write(w io.Writer, container, message string) { if l.ctx.Err() != nil { return } - if KeyboardManager != nil { - KeyboardManager.ClearKeyboardInfo() - } - p := l.getPresenter(container) timestamp := time.Now().Format(jsonmessage.RFC3339NanoFixed) for _, line := range strings.Split(message, "\n") { @@ -131,10 +127,6 @@ func (l *logConsumer) write(w io.Writer, container, message string) { _, _ = fmt.Fprintf(w, "%s%s\n", p.prefix, line) } } - - if KeyboardManager != nil { - KeyboardManager.PrintKeyboardInfo() - } } func (l *logConsumer) Status(container, msg string) { @@ -168,3 +160,31 @@ func (p *presenter) setPrefix(width int) { } p.prefix = p.colors(fmt.Sprintf("%-"+strconv.Itoa(width)+"s | ", p.name)) } + +type logDecorator struct { + decorated api.LogConsumer + Before func() + After func() +} + +func (l logDecorator) Log(containerName, message string) { + l.Before() + l.decorated.Log(containerName, message) + l.After() +} + +func (l logDecorator) Err(containerName, message string) { + l.Before() + l.decorated.Err(containerName, message) + l.After() +} + +func (l logDecorator) Status(container, msg string) { + l.Before() + l.decorated.Status(container, msg) + l.After() +} + +func (l logDecorator) Register(container string) { + l.decorated.Register(container) +} diff --git a/cmd/formatter/shortcut.go b/cmd/formatter/shortcut.go index b16fda855..7776e5f1e 100644 --- a/cmd/formatter/shortcut.go +++ b/cmd/formatter/shortcut.go @@ -22,7 +22,6 @@ import ( "fmt" "math" "os" - "reflect" "syscall" "time" @@ -70,12 +69,12 @@ func (ke *KeyboardError) error() string { } type KeyboardWatch struct { - Watching bool - Watcher Toggle - IsConfigured bool + Watching bool + Watcher Feature } -type Toggle interface { +// Feature is an compose feature that can be started/stopped by a menu command +type Feature interface { Start(context.Context) error Stop() error } @@ -90,31 +89,26 @@ const ( type LogKeyboard struct { kError KeyboardError - Watch KeyboardWatch + Watch *KeyboardWatch IsDockerDesktopActive bool logLevel KEYBOARD_LOG_LEVEL signalChannel chan<- os.Signal } -// FIXME(ndeloof) we should avoid use of such a global reference. see use in logConsumer -var KeyboardManager *LogKeyboard - -func NewKeyboardManager(isDockerDesktopActive bool, sc chan<- os.Signal, w bool, watcher Toggle) *LogKeyboard { - KeyboardManager = &LogKeyboard{ - Watch: KeyboardWatch{ - Watching: w, - Watcher: watcher, - IsConfigured: !reflect.ValueOf(watcher).IsNil(), - }, +func NewKeyboardManager(isDockerDesktopActive bool, sc chan<- os.Signal) *LogKeyboard { + return &LogKeyboard{ IsDockerDesktopActive: isDockerDesktopActive, logLevel: INFO, signalChannel: sc, } - return KeyboardManager } -func (lk *LogKeyboard) ClearKeyboardInfo() { - lk.clearNavigationMenu() +func (lk *LogKeyboard) Decorate(l api.LogConsumer) api.LogConsumer { + return logDecorator{ + decorated: l, + Before: lk.clearNavigationMenu, + After: lk.PrintKeyboardInfo, + } } func (lk *LogKeyboard) PrintKeyboardInfo() { @@ -185,7 +179,7 @@ func (lk *LogKeyboard) navigationMenu() string { watchInfo = navColor(" ") } isEnabled := " Enable" - if lk.Watch.Watching { + if lk.Watch != nil && lk.Watch.Watching { isEnabled = " Disable" } watchInfo = watchInfo + shortcutKeyColor("w") + navColor(isEnabled+" Watch") @@ -268,7 +262,7 @@ func (lk *LogKeyboard) keyboardError(prefix string, err error) { } func (lk *LogKeyboard) ToggleWatch(ctx context.Context, options api.UpOptions) { - if !lk.Watch.IsConfigured { + if lk.Watch == nil { return } if lk.Watch.Watching { @@ -299,7 +293,7 @@ func (lk *LogKeyboard) HandleKeyEvents(ctx context.Context, event keyboard.KeyEv case 'v': lk.openDockerDesktop(ctx, project) case 'w': - if !lk.Watch.IsConfigured { + if lk.Watch == nil { // we try to open watch docs if DD is installed if lk.IsDockerDesktopActive { lk.openDDWatchDocs(ctx, project) @@ -333,6 +327,13 @@ func (lk *LogKeyboard) HandleKeyEvents(ctx context.Context, event keyboard.KeyEv } } +func (lk *LogKeyboard) EnableWatch(enabled bool, watcher Feature) { + lk.Watch = &KeyboardWatch{ + Watching: enabled, + Watcher: watcher, + } +} + func allocateSpace(lines int) { for i := 0; i < lines; i++ { ClearLine() diff --git a/internal/tracing/keyboard_metrics.go b/internal/tracing/keyboard_metrics.go index 4043de8b7..2e5120fbe 100644 --- a/internal/tracing/keyboard_metrics.go +++ b/internal/tracing/keyboard_metrics.go @@ -22,15 +22,12 @@ import ( "go.opentelemetry.io/otel/attribute" ) -func KeyboardMetrics(ctx context.Context, enabled, isDockerDesktopActive, isWatchConfigured bool) { +func KeyboardMetrics(ctx context.Context, enabled, isDockerDesktopActive bool) { commandAvailable := []string{} if isDockerDesktopActive { commandAvailable = append(commandAvailable, "gui") commandAvailable = append(commandAvailable, "gui/composeview") } - if isWatchConfigured { - commandAvailable = append(commandAvailable, "watch") - } AddAttributeToSpan(ctx, attribute.Bool("navmenu.enabled", enabled), diff --git a/pkg/compose/up.go b/pkg/compose/up.go index 70ac9f917..93dc7e78e 100644 --- a/pkg/compose/up.go +++ b/pkg/compose/up.go @@ -70,15 +70,12 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM) defer signal.Stop(signalChan) var isTerminated atomic.Bool - printer := newLogPrinter(options.Start.Attach) - watcher, err := NewWatcher(project, options, s.watch) - if err != nil && options.Start.Watch { - return err - } - - var navigationMenu *formatter.LogKeyboard - var kEvents <-chan keyboard.KeyEvent + var ( + logConsumer = options.Start.Attach + navigationMenu *formatter.LogKeyboard + kEvents <-chan keyboard.KeyEvent + ) if options.Start.NavigationMenu { kEvents, err = keyboard.GetKeys(100) if err != nil { @@ -87,11 +84,23 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options } else { defer keyboard.Close() //nolint:errcheck isDockerDesktopActive := s.isDesktopIntegrationActive() - tracing.KeyboardMetrics(ctx, options.Start.NavigationMenu, isDockerDesktopActive, watcher != nil) - navigationMenu = formatter.NewKeyboardManager(isDockerDesktopActive, signalChan, options.Start.Watch, watcher) + tracing.KeyboardMetrics(ctx, options.Start.NavigationMenu, isDockerDesktopActive) + navigationMenu = formatter.NewKeyboardManager(isDockerDesktopActive, signalChan) + logConsumer = navigationMenu.Decorate(logConsumer) } } + watcher, err := NewWatcher(project, options, s.watch, logConsumer) + if err != nil && options.Start.Watch { + return err + } + + if navigationMenu != nil && watcher != nil { + navigationMenu.EnableWatch(options.Start.Watch, watcher) + } + + printer := newLogPrinter(logConsumer) + doneCh := make(chan bool) eg.Go(func() error { first := true diff --git a/pkg/compose/watch.go b/pkg/compose/watch.go index 9f9b3a1e8..80ed42acd 100644 --- a/pkg/compose/watch.go +++ b/pkg/compose/watch.go @@ -55,7 +55,7 @@ type Watcher struct { errCh chan error } -func NewWatcher(project *types.Project, options api.UpOptions, w WatchFunc) (*Watcher, error) { +func NewWatcher(project *types.Project, options api.UpOptions, w WatchFunc, consumer api.LogConsumer) (*Watcher, error) { for i := range project.Services { service := project.Services[i] @@ -65,7 +65,7 @@ func NewWatcher(project *types.Project, options api.UpOptions, w WatchFunc) (*Wa return &Watcher{ project: project, options: api.WatchOptions{ - LogTo: options.Start.Attach, + LogTo: consumer, Build: build, }, watchFn: w,