diff --git a/cmd/klotho/down.go b/cmd/klotho/down.go index 41bae14f..702af3e2 100644 --- a/cmd/klotho/down.go +++ b/cmd/klotho/down.go @@ -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) diff --git a/pkg/k2/language_host/ir_service.go b/pkg/k2/language_host/ir_service.go index 7356847e..c04f5080 100644 --- a/pkg/k2/language_host/ir_service.go +++ b/pkg/k2/language_host/ir_service.go @@ -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) diff --git a/pkg/k2/language_host/language_host.go b/pkg/k2/language_host/language_host.go index 9069df5e..0f33f152 100644 --- a/pkg/k2/language_host/language_host.go +++ b/pkg/k2/language_host/language_host.go @@ -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{}), } } @@ -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 } diff --git a/pkg/tui/log_core.go b/pkg/tui/log_core.go index d9604107..c2f478dd 100644 --- a/pkg/tui/log_core.go +++ b/pkg/tui/log_core.go @@ -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) diff --git a/pkg/tui/model.go b/pkg/tui/model.go index c0e999a9..06c3d0c8 100644 --- a/pkg/tui/model.go +++ b/pkg/tui/model.go @@ -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') @@ -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 } diff --git a/pkg/tui/verbosity.go b/pkg/tui/verbosity.go index 54261dda..de31cbcb 100644 --- a/pkg/tui/verbosity.go +++ b/pkg/tui/verbosity.go @@ -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