Files
nomad/command/ui/ui.go
James Rasell 4848bfadec cli: stream both stdout and stderr when following an alloc. (#16556)
This update changes the behaviour when following logs from an
allocation, so that both stdout and stderr files streamed when the
operator supplies the follow flag. The previous behaviour is held
when all other flags and situations are provided.

Co-authored-by: Luiz Aoqui <luiz@hashicorp.com>
2023-04-04 10:42:27 +01:00

115 lines
3.1 KiB
Go

package ui
import (
"errors"
"fmt"
"io"
"github.com/fatih/color"
"github.com/mitchellh/cli"
)
// LogUI is an implementation of the cli.Ui interface which can be used for
// logging outputs. It differs from cli.BasicUi in the only fact that it does
// not add a newline after each UI write.
type LogUI struct {
reader io.Reader
writer io.Writer
errorWriter io.Writer
// underlyingUI stores the basic UI that was used to create this logUI. It
// allows us to call the ask functions and not implement them again.
underlyingUI cli.Ui
isColor bool
outputColor cli.UiColor
infoColor cli.UiColor
errorColor cli.UiColor
warnColor cli.UiColor
}
// NewLogUI generates a new cli.Ui that can be used for commands that write log
// lines to the terminal. The caller is required to pass a cli.BasicUi so we
// have access to the underlying writers.
//
// Currently, the passed ui needs to be either *cli.ColoredUi or *cli.BasicUi
// to work correctly. If more are needed, please add them.
func NewLogUI(ui cli.Ui) (cli.Ui, error) {
var found bool
logUI := LogUI{}
if coloredUI, ok := ui.(*cli.ColoredUi); ok {
logUI.isColor = true
logUI.outputColor = coloredUI.OutputColor
logUI.infoColor = coloredUI.InfoColor
logUI.errorColor = coloredUI.ErrorColor
logUI.warnColor = coloredUI.WarnColor
logUI.underlyingUI = coloredUI.Ui
if basicUI, ok := coloredUI.Ui.(*cli.BasicUi); ok {
logUI.reader = basicUI.Reader
logUI.writer = basicUI.Writer
logUI.errorWriter = basicUI.ErrorWriter
found = true
}
} else if basicUI, ok := ui.(*cli.BasicUi); ok && !found {
logUI.reader = basicUI.Reader
logUI.writer = basicUI.Writer
logUI.errorWriter = basicUI.ErrorWriter
logUI.underlyingUI = basicUI
found = true
}
if !found {
return nil, errors.New("failed to generate logging UI")
}
return &logUI, nil
}
// Ask implements the Ask function of the cli.Ui interface.
func (l *LogUI) Ask(query string) (string, error) {
return l.underlyingUI.Ask(l.colorize(query, l.outputColor))
}
// AskSecret implements the AskSecret function of the cli.Ui interface.
func (l *LogUI) AskSecret(query string) (string, error) {
return l.underlyingUI.AskSecret(l.colorize(query, l.outputColor))
}
// Output implements the Output function of the cli.Ui interface.
func (l *LogUI) Output(message string) {
_, _ = fmt.Fprint(l.writer, l.colorize(message, l.outputColor))
}
// Info implements the Info function of the cli.Ui interface.
func (l *LogUI) Info(message string) { l.Output(l.colorize(message, l.infoColor)) }
// Error implements the Error function of the cli.Ui interface.
func (l *LogUI) Error(message string) {
w := l.writer
if l.errorWriter != nil {
w = l.errorWriter
}
_, _ = fmt.Fprint(w, l.colorize(message, l.errorColor))
}
// Warn implements the Warn function of the cli.Ui interface.
func (l *LogUI) Warn(message string) { l.Error(l.colorize(message, l.warnColor)) }
func (l *LogUI) colorize(message string, uc cli.UiColor) string {
if !l.isColor {
return message
}
attr := []color.Attribute{color.Attribute(uc.Code)}
if uc.Bold {
attr = append(attr, color.Bold)
}
return color.New(attr...).SprintFunc()(message)
}