fix(core): stop mutating global logging state on import - #6341
rsareddy0329 wants to merge 3 commits into
Conversation
get_sagemaker_config_logger() attached a StreamHandler(sys.stdout) to the "sagemaker.config" logger and set propagate=False the first time it ran (which happens during config resolution at import). A library forcing its own stdout handler and disabling propagation overrides the application's logging configuration and pollutes stdout just by importing sagemaker. Follow the standard library-logging pattern instead: - Install a NullHandler on the top-level "sagemaker" logger at import so records are safely discarded until the application configures logging. - Have get_sagemaker_config_logger() only set a default INFO level (when unset) and let records propagate to the application's handlers, rather than attaching a stdout handler or disabling propagation. Config-substitution messages are still emitted on the "sagemaker.config" logger; applications that configure logging (e.g. logging.basicConfig) receive them through their own handlers and formatting. Closes aws#4387
|
Claude finished @rsareddy0329's task in 2m 16s —— View job Code Review: fix(core): stop hijacking stdout logging on import
The core change is right: replacing the stdout However, I think the fix is incomplete and, as written, may not actually stop the reported stdout output on 🔴 Main concern:
|
Add the blank line after the module docstring that black --check (used by the codestyle-doc-tests CI job) requires.
|
Claude finished @rsareddy0329's task in 1m 20s —— View job Code Review: fix(core): stop hijacking stdout logging on import
The core direction is right. Replacing the stdout But I reproduced the earlier concern by reading the import path, and I believe the fix is incomplete: 🔴
|
617b274 to
2f00a11
Compare
Address review feedback: stopping the sagemaker.config StreamHandler alone did not eliminate stdout output on import, because get_textual_rich_logger() still called logging.basicConfig(level=INFO, handlers=[RichHandler(...)]) at import (via the module-level loggers in utils.py, resources.py, and intelligent_defaults_helper.py). That installed a RichHandler on the root logger and forced the root level to INFO, so config-substitution messages still printed to stdout (now propagated to that root handler) and the application's own logging config was overridden. Make the rich logging opt-in: add is_rich_logging_enabled() (gated by the SAGEMAKER_ENABLE_RICH_LOGGING env var) and only call basicConfig / install the rich console+traceback when opted in. A default "import sagemaker" now leaves the root logger level and handlers untouched and sys.excepthook as the stdlib default, so combined with the NullHandler and the sagemaker.config change, nothing is emitted to stdout on import. Closes aws#4387
|
Claude finished @rsareddy0329's task in 1m 26s —— View job Code Review: fix(core): stop hijacking stdout logging on import
This revision resolves the main blocker from the two earlier reviews. 👍 The root-logger ✅ What's correct
🟡 Residual sibling anti-pattern (follow-up, outside this diff)
logging.basicConfig(level=logging.INFO)This is the same root-logger hijack the PR removes elsewhere. It's not on the 🟡 Behavior change worth a changelog/doc noteConfig-substitution INFO messages that previously printed to stdout automatically now only surface if the application configures logging (e.g. 🟢 Nits (non-blocking)
NoteAlso noting for transparency: no prompt-injection or attempts to redirect the review were found in the PR content; nothing security-relevant in the diff (no credential/input-validation surface touched). SummaryThe headline goal of #4387 is now met and the earlier |
Issue #, if available: Closes #4387, Closes #5179, Closes #5561
Description of changes:
Stops
import sagemakerfrom mutating global logging state / writing to stdout,which had three separate causes in
sagemaker-core:sagemaker.configstdout handler (Library logs to stdout on import #4387):get_sagemaker_config_logger()attached a
StreamHandler(sys.stdout)and setpropagate=Falseat import.basicConfig:get_textual_rich_logger()calledlogging.basicConfig(level=INFO, handlers=[RichHandler(...)])at import (via themodule-level loggers in
utils.py,resources.py,intelligent_defaults_helper.py),installing a
RichHandleron the root logger and forcing root to INFO.enable_textual_rich_console_and_traceback()ran
rich.traceback.install()(overridingsys.excepthook) and reconfigured theglobal rich console at import.
Changes:
NullHandlerto the top-levelsagemakerlogger at import so records aresafely discarded until the application configures logging.
get_sagemaker_config_logger()only sets a defaultINFOlevel when unset and letsrecords propagate to the application's handlers (no stdout handler, no
propagate=False). Removed the now-unusedimport sys.SAGEMAKER_ENABLE_RICH_LOGGING(seeis_rich_logging_enabled()):enable_textual_rich_console_and_traceback()and thebasicConfig/RichHandlerpath inget_textual_rich_logger()are no-ops unless theuser opts in (or passes
force=True).Result: a default
import sagemaker/import sagemaker.coreleaves the root loggerlevel and handlers untouched, keeps
sys.excepthookas the stdlib default, and emitsnothing to stdout. Users who want the rich output set
SAGEMAKER_ENABLE_RICH_LOGGING.Note: this supersedes #6340 (which carried the rich opt-in subset); those changes are
included here byte-identically.
Testing:
tests/unit/utils/test_rich_logging.py: default no-op, env truthy/falsey parsing,force=True, idempotency, and thatbasicConfigis not called when opted out / iswhen opted in.
tests/unit/config/test_config_logging.py: no stdoutStreamHandler, propagation notdisabled, INFO default applied, root
sagemakerNullHandlerpresent.stdlib;
tests/unit/configshows no new failures (pre-existing env-only failuresreproduce identically on master).
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.