@@ -31,6 +31,8 @@ import (
3131 tea "github.com/charmbracelet/bubbletea"
3232 "github.com/charmbracelet/lipgloss"
3333 "github.com/go-logr/logr"
34+ "go.uber.org/zap"
35+ "go.uber.org/zap/zapcore"
3436
3537 "github.com/livekit/livekit-cli/v2/pkg/util"
3638 "github.com/livekit/protocol/livekit"
@@ -41,12 +43,9 @@ import (
4143
4244func runSimulateTUI (config * simulateConfig ) error {
4345 // SDK/protocol log lines go to stderr behind the alt screen and would all
44- // spill into the terminal when the TUI exits; the agent log file and the
45- // run report carry the diagnostics instead.
46- discard := logger .LogRLogger (logr .Discard ())
47- logger .SetLogger (discard , "lk" )
48- lksdk .SetLogger (discard )
49- log .SetOutput (io .Discard )
46+ // spill into the terminal when the TUI exits; capture them in a file
47+ // instead, surfaced as a path on exit like the agent log.
48+ cliLogPath := redirectLogsToFile ()
5049
5150 launcher := launchSimulationAgent (config )
5251 m := newSimulateModel (config , launcher )
@@ -82,6 +81,10 @@ func runSimulateTUI(config *simulateConfig) error {
8281 }
8382 }
8483
84+ if cliLogPath != "" {
85+ out .Statusf ("CLI logs: %s" , cliLogPath )
86+ }
87+
8588 // The summary (which carries the chat transcripts) may have landed after
8689 // the TUI's last poll; refresh once so the report includes it.
8790 if m .run != nil && m .run .Summary == nil && m .runID != "" {
@@ -114,6 +117,36 @@ func runSimulateTUI(config *simulateConfig) error {
114117 return nil
115118}
116119
120+ // redirectLogsToFile points the protocol/SDK/stdlib loggers at a temp file
121+ // and returns its path; on failure the logs are discarded (they would
122+ // otherwise corrupt the TUI and spill on exit).
123+ func redirectLogsToFile () string {
124+ f , err := os .CreateTemp ("" , "lk-simulate-cli-*.log" )
125+ if err != nil {
126+ discard := logger .LogRLogger (logr .Discard ())
127+ logger .SetLogger (discard , "lk" )
128+ lksdk .SetLogger (discard )
129+ log .SetOutput (io .Discard )
130+ return ""
131+ }
132+ core := zapcore .NewCore (
133+ zapcore .NewConsoleEncoder (zap .NewDevelopmentEncoderConfig ()),
134+ zapcore .AddSync (f ),
135+ zapcore .InfoLevel ,
136+ )
137+ zl , err := logger .FromZapLogger (zap .New (core ), & logger.Config {Level : "info" })
138+ if err != nil {
139+ discard := logger .LogRLogger (logr .Discard ())
140+ logger .SetLogger (discard , "lk" )
141+ lksdk .SetLogger (discard )
142+ } else {
143+ logger .SetLogger (zl , "lk" )
144+ lksdk .SetLogger (zl )
145+ }
146+ log .SetOutput (f )
147+ return f .Name ()
148+ }
149+
117150// --- Styles ---
118151
119152// Color styles are functions so they read the active theme palette at render time and
0 commit comments