Silence extension process stderr logs to prevent call-stack-like error output#6939
Silence extension process stderr logs to prevent call-stack-like error output#6939hemarina wants to merge 2 commits intoAzure:mainfrom
Conversation
…r output Extension processes emit verbose gRPC MessageBroker trace logs via Go's log.Printf, which defaults to os.Stderr. Since azd captures extension stderr and includes it in error messages, these internal diagnostics appear to users as call-stack-like error output. Silence the global logger in ExtensionHost.Run() by setting log.SetOutput(io.Discard) unless AZD_EXT_DEBUG=true is set. This ensures backward compatibility with extensions compiled against older SDK versions that still use log.Printf directly.
There was a problem hiding this comment.
Pull request overview
This PR updates the extension host startup path to suppress noisy internal gRPC broker trace logs from appearing in user-facing stderr output, while keeping diagnostics available when extension debugging is enabled.
Changes:
- Silences the global Go
logoutput in extension processes by default vialog.SetOutput(io.Discard). - Keeps stderr logging enabled when
AZD_EXT_DEBUGindicates debugging mode.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| // TestExtensionHost_RunSilencesLog tests that Run() silences the global logger | ||
| // when AZD_EXT_DEBUG is not set, preventing internal gRPC broker trace logs | ||
| // from appearing in extension stderr. | ||
| // These tests mutate global state (log output, env vars) and must NOT run in parallel. | ||
| func TestExtensionHost_RunSilencesLog(t *testing.T) { |
There was a problem hiding this comment.
This test mutates global process state (standard logger output and AZD_EXT_DEBUG), but other tests in this same file/package are marked t.Parallel() and call ExtensionHost.Run(), which now also mutates the global logger. That means this test can still run concurrently with those parallel tests and become flaky due to cross-test interference. Consider removing t.Parallel() from tests that call ExtensionHost.Run(), or guarding all log/env mutations with a shared package-level mutex so tests that touch global state cannot overlap.
| // "1", "t", "TRUE", "true", etc.). | ||
| if isDebug, err := strconv.ParseBool(os.Getenv("AZD_EXT_DEBUG")); err != nil || !isDebug { | ||
| log.SetOutput(io.Discard) | ||
| } |
There was a problem hiding this comment.
ExtensionHost.Run() now sets the global standard logger output to io.Discard and never restores it. In real extension binaries that exit after Run returns this is mostly harmless, but it creates surprising global side effects for unit tests (especially those running in parallel) and for any program that might call Run() and continue doing work afterward. Consider saving the original log writer and restoring it via defer when Run() returns, or clearly scoping the silencing to the broker logger rather than the process-wide default logger.
| // "1", "t", "TRUE", "true", etc.). | |
| if isDebug, err := strconv.ParseBool(os.Getenv("AZD_EXT_DEBUG")); err != nil || !isDebug { | |
| log.SetOutput(io.Discard) | |
| } | |
| // "1", "t", "TRUE", "true", etc.). | |
| originalLogWriter := log.Default().Writer() | |
| restoreLogOutput := false | |
| if isDebug, err := strconv.ParseBool(os.Getenv("AZD_EXT_DEBUG")); err != nil || !isDebug { | |
| log.SetOutput(io.Discard) | |
| restoreLogOutput = true | |
| } | |
| if restoreLogOutput { | |
| defer log.SetOutput(originalLogWriter) | |
| } |
Azure Dev CLI Install InstructionsInstall scriptsMacOS/Linux
bash: pwsh: WindowsPowerShell install MSI install Standalone Binary
MSI
Documentationlearn.microsoft.com documentationtitle: Azure Developer CLI reference
|
Problem
Extension processes emit verbose gRPC MessageBroker trace logs via Go's
log.Printf, which defaults toos.Stderr. Since azd captures extension stderr and includes it in error messages (viaExitError.Error()), these internal diagnostics appear to users as call-stack-like error output.Root Cause
The
MessageBrokerinpkg/grpcbrokercontains ~40 unconditionallog.Printfcalls for tracing. In the main azd process, these are silenced bylog.SetOutput(io.Discard)inmain.go. However, extension processes are separate Go binaries that never calllog.SetOutput, so all broker trace logs go toos.Stderrby default.Fix
Silence the global logger in
ExtensionHost.Run()by callinglog.SetOutput(io.Discard)at startup. This:AZD_EXT_DEBUG=trueis set for extension debuggingTesting
AZD_EXT_DEBUG=truestill enables diagnostic logging