-
Notifications
You must be signed in to change notification settings - Fork 274
Silence extension process stderr logs to prevent call-stack-like error output #6939
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,8 +4,12 @@ | |
| package azdext | ||
|
|
||
| import ( | ||
| "bytes" | ||
| "context" | ||
| "errors" | ||
| "io" | ||
| "log" | ||
| "os" | ||
| "sync" | ||
| "testing" | ||
| "time" | ||
|
|
@@ -666,3 +670,91 @@ func TestExtensionHost_MultipleRegistrationErrors(t *testing.T) { | |
| mockServiceTargetManager.AssertExpectations(t) | ||
| mockFrameworkServiceManager.AssertExpectations(t) | ||
| } | ||
|
|
||
| // 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) { | ||
|
Comment on lines
+674
to
+678
|
||
| // Save and restore global log output | ||
| originalOutput := log.Writer() | ||
| defer log.SetOutput(originalOutput) | ||
|
|
||
| // Save and restore AZD_EXT_DEBUG env var | ||
| originalDebug, hadDebug := os.LookupEnv("AZD_EXT_DEBUG") | ||
| defer func() { | ||
| if hadDebug { | ||
| os.Setenv("AZD_EXT_DEBUG", originalDebug) | ||
| } else { | ||
| os.Unsetenv("AZD_EXT_DEBUG") | ||
| } | ||
| }() | ||
|
|
||
| t.Run("silences log output by default", func(t *testing.T) { | ||
| // Reset log to a known non-discard writer | ||
| var buf bytes.Buffer | ||
| log.SetOutput(&buf) | ||
|
|
||
| // Ensure AZD_EXT_DEBUG is not set | ||
| os.Unsetenv("AZD_EXT_DEBUG") | ||
|
|
||
| // Setup minimal extension host with no registrations | ||
| client := newTestAzdClient() | ||
| runner := NewExtensionHost(client) | ||
|
|
||
| err := runner.Run(context.Background()) | ||
| require.NoError(t, err) | ||
|
|
||
| // After Run(), global log should be silenced | ||
| assert.Equal(t, io.Discard, log.Writer(), "log output should be io.Discard when AZD_EXT_DEBUG is not set") | ||
|
|
||
| // Verify log.Printf output is actually discarded | ||
| log.Printf("this should be discarded") | ||
| assert.Empty(t, buf.String(), "log output should not appear in the buffer after silencing") | ||
| }) | ||
|
|
||
| t.Run("silences log output when AZD_EXT_DEBUG is empty", func(t *testing.T) { | ||
| var buf bytes.Buffer | ||
| log.SetOutput(&buf) | ||
|
|
||
| os.Setenv("AZD_EXT_DEBUG", "") | ||
|
|
||
| client := newTestAzdClient() | ||
| runner := NewExtensionHost(client) | ||
|
|
||
| err := runner.Run(context.Background()) | ||
| require.NoError(t, err) | ||
|
|
||
| assert.Equal(t, io.Discard, log.Writer(), "log output should be io.Discard when AZD_EXT_DEBUG is empty") | ||
| }) | ||
|
|
||
| t.Run("silences log output when AZD_EXT_DEBUG is false", func(t *testing.T) { | ||
| var buf bytes.Buffer | ||
| log.SetOutput(&buf) | ||
|
|
||
| os.Setenv("AZD_EXT_DEBUG", "false") | ||
|
|
||
| client := newTestAzdClient() | ||
| runner := NewExtensionHost(client) | ||
|
|
||
| err := runner.Run(context.Background()) | ||
| require.NoError(t, err) | ||
|
|
||
| assert.Equal(t, io.Discard, log.Writer(), "log output should be io.Discard when AZD_EXT_DEBUG is false") | ||
| }) | ||
|
|
||
| t.Run("silences log output when AZD_EXT_DEBUG is invalid", func(t *testing.T) { | ||
| var buf bytes.Buffer | ||
| log.SetOutput(&buf) | ||
|
|
||
| os.Setenv("AZD_EXT_DEBUG", "notabool") | ||
|
|
||
| client := newTestAzdClient() | ||
| runner := NewExtensionHost(client) | ||
|
|
||
| err := runner.Run(context.Background()) | ||
| require.NoError(t, err) | ||
|
|
||
| assert.Equal(t, io.Discard, log.Writer(), "log output should be io.Discard when AZD_EXT_DEBUG is invalid") | ||
| }) | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.