2023-04-06 10:54:58 +00:00
|
|
|
package cmdio
|
2023-03-29 12:58:09 +00:00
|
|
|
|
|
|
|
import (
|
2023-04-06 10:54:58 +00:00
|
|
|
"bufio"
|
2023-04-18 14:55:06 +00:00
|
|
|
"context"
|
2023-03-29 12:58:09 +00:00
|
|
|
"encoding/json"
|
2023-04-18 15:13:49 +00:00
|
|
|
"fmt"
|
2023-03-29 12:58:09 +00:00
|
|
|
"io"
|
|
|
|
"os"
|
2023-08-15 14:50:20 +00:00
|
|
|
"strings"
|
2023-03-29 12:58:09 +00:00
|
|
|
|
2023-05-16 16:35:39 +00:00
|
|
|
"github.com/databricks/cli/libs/flags"
|
2023-03-29 12:58:09 +00:00
|
|
|
)
|
|
|
|
|
2023-04-18 14:55:06 +00:00
|
|
|
// This is the interface for all io interactions with a user
|
2023-03-29 12:58:09 +00:00
|
|
|
type Logger struct {
|
2023-04-18 14:55:06 +00:00
|
|
|
// Mode for the logger. One of (append, inplace, json).
|
2023-04-06 10:54:58 +00:00
|
|
|
Mode flags.ProgressLogFormat
|
|
|
|
|
2023-04-18 14:55:06 +00:00
|
|
|
// Input stream (eg. stdin). Answers to questions prompted using the Ask() method
|
|
|
|
// are read from here
|
2023-04-06 10:54:58 +00:00
|
|
|
Reader bufio.Reader
|
2023-04-18 14:55:06 +00:00
|
|
|
|
|
|
|
// Output stream where the logger writes to
|
2023-03-29 12:58:09 +00:00
|
|
|
Writer io.Writer
|
|
|
|
|
2023-04-18 14:55:06 +00:00
|
|
|
// If true, indicates no events have been printed by the logger yet. Used
|
|
|
|
// by inplace logging for formatting
|
2023-03-29 12:58:09 +00:00
|
|
|
isFirstEvent bool
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewLogger(mode flags.ProgressLogFormat) *Logger {
|
|
|
|
return &Logger{
|
|
|
|
Mode: mode,
|
|
|
|
Writer: os.Stderr,
|
2023-04-06 10:54:58 +00:00
|
|
|
Reader: *bufio.NewReader(os.Stdin),
|
2023-03-29 12:58:09 +00:00
|
|
|
isFirstEvent: true,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-18 14:55:06 +00:00
|
|
|
func Default() *Logger {
|
|
|
|
return &Logger{
|
|
|
|
Mode: flags.ModeAppend,
|
|
|
|
Writer: os.Stderr,
|
|
|
|
Reader: *bufio.NewReader(os.Stdin),
|
|
|
|
isFirstEvent: true,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func Log(ctx context.Context, event Event) {
|
|
|
|
logger, ok := FromContext(ctx)
|
|
|
|
if !ok {
|
|
|
|
logger = Default()
|
|
|
|
}
|
|
|
|
logger.Log(event)
|
|
|
|
}
|
|
|
|
|
|
|
|
func LogString(ctx context.Context, message string) {
|
|
|
|
logger, ok := FromContext(ctx)
|
|
|
|
if !ok {
|
|
|
|
logger = Default()
|
|
|
|
}
|
|
|
|
logger.Log(&MessageEvent{
|
|
|
|
Message: message,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-04-24 10:11:52 +00:00
|
|
|
func LogError(ctx context.Context, err error) {
|
|
|
|
logger, ok := FromContext(ctx)
|
|
|
|
if !ok {
|
|
|
|
logger = Default()
|
|
|
|
}
|
|
|
|
logger.Log(&ErrorEvent{
|
|
|
|
Error: err.Error(),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-08-15 14:50:20 +00:00
|
|
|
func Ask(ctx context.Context, question, defaultVal string) (string, error) {
|
2023-04-18 14:55:06 +00:00
|
|
|
logger, ok := FromContext(ctx)
|
|
|
|
if !ok {
|
|
|
|
logger = Default()
|
|
|
|
}
|
2023-08-15 14:50:20 +00:00
|
|
|
return logger.Ask(question, defaultVal)
|
2023-04-18 14:55:06 +00:00
|
|
|
}
|
|
|
|
|
2023-08-15 14:50:20 +00:00
|
|
|
func AskYesOrNo(ctx context.Context, question string) (bool, error) {
|
|
|
|
logger, ok := FromContext(ctx)
|
|
|
|
if !ok {
|
|
|
|
logger = Default()
|
2023-04-18 15:13:49 +00:00
|
|
|
}
|
|
|
|
|
2023-08-09 09:22:42 +00:00
|
|
|
// Add acceptable answers to the question prompt.
|
2023-08-15 14:50:20 +00:00
|
|
|
question += ` [y/n]`
|
2023-04-06 10:54:58 +00:00
|
|
|
|
2023-08-15 14:50:20 +00:00
|
|
|
// Ask the question
|
|
|
|
ans, err := logger.Ask(question, "")
|
2023-04-06 10:54:58 +00:00
|
|
|
if err != nil {
|
|
|
|
return false, err
|
|
|
|
}
|
|
|
|
|
2023-08-15 14:50:20 +00:00
|
|
|
if ans == "y" {
|
2023-04-06 10:54:58 +00:00
|
|
|
return true, nil
|
|
|
|
}
|
2023-08-15 14:50:20 +00:00
|
|
|
return false, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *Logger) Ask(question string, defaultVal string) (string, error) {
|
|
|
|
if l.Mode == flags.ModeJson {
|
|
|
|
return "", fmt.Errorf("question prompts are not supported in json mode")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add default value to question prompt.
|
|
|
|
if defaultVal != "" {
|
|
|
|
question += fmt.Sprintf(` [%s]`, defaultVal)
|
|
|
|
}
|
|
|
|
question += `: `
|
|
|
|
|
|
|
|
// print prompt
|
|
|
|
_, err := l.Writer.Write([]byte(question))
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
// read user input. Trim new line characters
|
|
|
|
ans, err := l.Reader.ReadString('\n')
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
ans = strings.Trim(ans, "\n\r")
|
|
|
|
|
|
|
|
// Return default value if user just presses enter
|
|
|
|
if ans == "" {
|
|
|
|
return defaultVal, nil
|
|
|
|
}
|
|
|
|
return ans, nil
|
2023-04-06 10:54:58 +00:00
|
|
|
}
|
|
|
|
|
2023-04-18 12:20:35 +00:00
|
|
|
func (l *Logger) writeJson(event Event) {
|
|
|
|
b, err := json.MarshalIndent(event, "", " ")
|
|
|
|
if err != nil {
|
|
|
|
// we panic because there we cannot catch this in jobs.RunNowAndWait
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
l.Writer.Write([]byte(b))
|
|
|
|
l.Writer.Write([]byte("\n"))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *Logger) writeAppend(event Event) {
|
|
|
|
l.Writer.Write([]byte(event.String()))
|
|
|
|
l.Writer.Write([]byte("\n"))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *Logger) writeInplace(event Event) {
|
|
|
|
if l.isFirstEvent {
|
|
|
|
// save cursor location
|
|
|
|
l.Writer.Write([]byte("\033[s"))
|
|
|
|
}
|
|
|
|
|
|
|
|
// move cursor to saved location
|
|
|
|
l.Writer.Write([]byte("\033[u"))
|
|
|
|
|
|
|
|
// clear from cursor to end of screen
|
|
|
|
l.Writer.Write([]byte("\033[0J"))
|
|
|
|
|
|
|
|
l.Writer.Write([]byte(event.String()))
|
|
|
|
l.Writer.Write([]byte("\n"))
|
|
|
|
l.isFirstEvent = false
|
|
|
|
}
|
|
|
|
|
2023-03-29 12:58:09 +00:00
|
|
|
func (l *Logger) Log(event Event) {
|
|
|
|
switch l.Mode {
|
|
|
|
case flags.ModeInplace:
|
2023-04-18 12:20:35 +00:00
|
|
|
if event.IsInplaceSupported() {
|
|
|
|
l.writeInplace(event)
|
|
|
|
} else {
|
|
|
|
l.writeAppend(event)
|
2023-03-29 12:58:09 +00:00
|
|
|
}
|
2023-04-14 11:06:04 +00:00
|
|
|
|
2023-03-29 12:58:09 +00:00
|
|
|
case flags.ModeJson:
|
2023-04-18 12:20:35 +00:00
|
|
|
l.writeJson(event)
|
2023-03-29 12:58:09 +00:00
|
|
|
|
|
|
|
case flags.ModeAppend:
|
2023-04-18 12:20:35 +00:00
|
|
|
l.writeAppend(event)
|
2023-03-29 12:58:09 +00:00
|
|
|
|
|
|
|
default:
|
|
|
|
// we panic because errors are not captured in some log sides like
|
|
|
|
// jobs.RunNowAndWait
|
|
|
|
panic("unknown progress logger mode: " + l.Mode.String())
|
|
|
|
}
|
2023-04-18 12:20:35 +00:00
|
|
|
|
2023-03-29 12:58:09 +00:00
|
|
|
}
|