Skip to content

Commit

Permalink
Implements error rendering feedback
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidSeptimus-Klotho committed Aug 16, 2024
1 parent e9f8535 commit 9ce6af9
Show file tree
Hide file tree
Showing 6 changed files with 40 additions and 31 deletions.
15 changes: 7 additions & 8 deletions cmd/klotho/down.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,20 +62,19 @@ func getProjectPath(ctx context.Context, inputPath string) (string, error) {
log.Debug("Waiting for Python server to start")
if downConfig.debugMode != "" {
// Don't add a timeout in case there are breakpoints in the language host before an address is printed
select {
case <-srvState.HasAddr:
case <-srvState.HasErr:
return "", srvState.Error
}
<-srvState.Done
} else {
select {
case <-srvState.HasAddr:
case <-srvState.Done:
case <-time.After(30 * time.Second):
return "", errors.New("timeout waiting for Python server to start")
case <-srvState.HasErr:
return "", srvState.Error
}
}

if srvState.Error != nil {
return "", srvState.Error
}

conn, err := grpc.NewClient(srvState.Address, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return "", fmt.Errorf("failed to connect to Python server: %w", err)
Expand Down
14 changes: 6 additions & 8 deletions pkg/k2/language_host/ir_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,21 +34,19 @@ func (irs *LanguageHost) Start(ctx context.Context, debug DebugConfig, pythonPat
log.Debug("Waiting for Python server to start")
if debug.Enabled() {
// Don't add a timeout in case there are breakpoints in the language host before an address is printed
select {
case <-srvState.HasAddr:
case <-srvState.HasErr:
return srvState.Error
}
<-srvState.Done
} else {
select {
case <-srvState.HasAddr:
case <-srvState.Done:
case <-time.After(30 * time.Second):
return errors.New("timeout waiting for Python server to start")
case <-srvState.HasErr:
return srvState.Error
}
}

if srvState.Error != nil {
return srvState.Error
}

irs.conn, err = grpc.NewClient(srvState.Address, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return fmt.Errorf("failed to connect to Python server: %w", err)
Expand Down
29 changes: 16 additions & 13 deletions pkg/k2/language_host/language_host.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,13 @@ type ServerState struct {
Log *zap.SugaredLogger
Address string
Error error
HasErr chan error
HasAddr chan struct{}
Done chan struct{}
}

func NewServerState(log *zap.SugaredLogger) *ServerState {
return &ServerState{
Log: log,
HasErr: make(chan error),
HasAddr: make(chan struct{}),
Log: log,
Done: make(chan struct{}),
}
}

Expand All @@ -45,20 +43,25 @@ func (f *ServerState) Write(b []byte) (int, error) {
return len(b), nil
}
s := string(b)
matches := listenOnPattern.FindStringSubmatch(s)

// captures the gRPC server address
matches := exceptionPattern.FindStringSubmatch(s)
if len(matches) >= 2 {
f.Error = errors.New(strings.TrimSpace(matches[1]))
f.Log.Debug(s)
}

// captures a fatal error in the language host that occurs before the address is printed to stdout
matches = listenOnPattern.FindStringSubmatch(s)
if len(matches) >= 2 {
// address is the first match
f.Address = matches[1]
f.Log.Debugf("Found language host listening on %s", f.Address)
close(f.HasAddr)
}
matches = exceptionPattern.FindStringSubmatch(s)
if len(matches) >= 2 {
f.Error = errors.New(strings.TrimSpace(matches[1]))
close(f.HasErr)
close(f.HasAddr)
}

if f.Address != "" || f.Error != nil {
close(f.Done)
}
return len(b), nil
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/tui/log_core.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func (c *LogCore) With(f []zapcore.Field) zapcore.Core {
if c.verbosity.CombineLogs() {
field.AddTo(nc.enc)
}
// else (if the field is the construct and we're not combining errors) don't add it to the encoder
// else (if the field is the construct, and we're not combining logs) don't add it to the encoder
// because the log lines will already be in its own construct section of the output.
} else {
field.AddTo(nc.enc)
Expand Down
9 changes: 9 additions & 0 deletions pkg/tui/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,9 @@ func (m *model) viewCompact() string {
}
}
}
if len(m.constructs) > 0 && len(m.errors) > 0 {
sb.WriteRune('\n')
}
for _, log := range m.errors {
sb.WriteString(log)
sb.WriteRune('\n')
Expand Down Expand Up @@ -326,6 +329,12 @@ func (m *model) viewVerbose() string {
m.renderConstructBox(lines, sb)
sb.WriteRune('\n') // extra newline to separate constructs
}

for _, log := range m.errors {
sb.WriteString(log)
sb.WriteRune('\n')
}

s := sb.String()
return s
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/tui/verbosity.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ func (v Verbosity) LogLevel() zapcore.Level {
}
}

// CombineLogs controls whether to show all errors commingled in the TUI.
// CombineLogs controls whether to show all logs commingled in the TUI.
// In other words, sorted by timestamp, not grouped by construct.
func (v Verbosity) CombineLogs() bool {
return VerbosityDebugMore == v
Expand Down

0 comments on commit 9ce6af9

Please sign in to comment.