From e58d1aef8fa8e659f5a9e32849b3376b78ba6d00 Mon Sep 17 00:00:00 2001 From: Krish Date: Thu, 21 May 2026 22:01:11 +0000 Subject: [PATCH] Route awscrt logs through Python logging --- .../enhancement-logging-43326.json | 5 +++++ awscli/clidriver.py | 2 +- awscli/logger.py | 11 ++++++----- tests/unit/test_clidriver.py | 17 ++++++----------- tests/unit/test_logger.py | 18 ++++++------------ 5 files changed, 24 insertions(+), 29 deletions(-) create mode 100644 .changes/next-release/enhancement-logging-43326.json diff --git a/.changes/next-release/enhancement-logging-43326.json b/.changes/next-release/enhancement-logging-43326.json new file mode 100644 index 000000000000..b3fadc24d7fd --- /dev/null +++ b/.changes/next-release/enhancement-logging-43326.json @@ -0,0 +1,5 @@ +{ + "type": "enhancement", + "category": "logging", + "description": "Add CRT logging integration so awscrt logs are routed through Python's logging module under the awscrt logger and emitted alongside botocore/awscli debug logs" +} diff --git a/awscli/clidriver.py b/awscli/clidriver.py index 643331c50d46..5683deb53ce5 100644 --- a/awscli/clidriver.py +++ b/awscli/clidriver.py @@ -626,7 +626,7 @@ def _handle_top_level_args(self, args): self._set_logging(getattr(args, 'debug', False)) def _set_logging(self, debug): - loggers_list = ['botocore', 'awscli', 's3transfer', 'urllib3'] + loggers_list = ['botocore', 'awscli', 's3transfer', 'urllib3', 'awscrt'] if debug: for logger_name in loggers_list: set_stream_logger( diff --git a/awscli/logger.py b/awscli/logger.py index 32d02b263330..19ad7e8f579e 100644 --- a/awscli/logger.py +++ b/awscli/logger.py @@ -12,7 +12,7 @@ # language governing permissions and limitations under the License. import logging -import awscrt.io +import awscrt.logging LOG_FORMAT = '%(asctime)s - %(name)s - %(levelname)s - %(message)s' @@ -21,8 +21,9 @@ # Initializing CRT logging isn't thread-safe, but setting/updating it is. # So we always initialize logging when this module is imported so # subsequent enable/disable CRT logging calls can safely assume it's - # already initialized. - awscrt.io.init_logging(awscrt.io.LogLevel.NoLogs, 'stderr') + # already initialized. CRT logs are routed through Python's logging + # module under the ``awscrt`` logger hierarchy. + awscrt.logging.init_logging(logging.NOTSET) except RuntimeError: # Calling `init_logging` more than once raises a Runtime exception. # Even though normal usage shouldn't call it more than once in the @@ -91,8 +92,8 @@ def remove_stream_logger(logger_name): def enable_crt_logging(): - awscrt.io.set_log_level(awscrt.io.LogLevel.Debug) + awscrt.logging.set_log_level(logging.DEBUG) def disable_crt_logging(): - awscrt.io.set_log_level(awscrt.io.LogLevel.NoLogs) + awscrt.logging.set_log_level(logging.NOTSET) diff --git a/tests/unit/test_clidriver.py b/tests/unit/test_clidriver.py index cd5315212728..99c786ef4a54 100644 --- a/tests/unit/test_clidriver.py +++ b/tests/unit/test_clidriver.py @@ -19,7 +19,6 @@ import sys from collections import namedtuple -import awscrt.io import botocore.model import pytest from botocore import xform_name @@ -437,22 +436,18 @@ def test_cli_driver_can_remove_log_handlers(self): assert rc == 252 assert 1 == fake_stderr.getvalue().count('CLI version:') - @mock.patch('awscrt.io.set_log_level') - def test_debug_enables_crt_logging(self, mock_init_logging): + @mock.patch('awscrt.logging.set_log_level') + def test_debug_enables_crt_logging(self, mock_set_log_level): with contextlib.redirect_stderr(io.StringIO()): self.driver.main( ['s3', 'list-objects', '--bucket', 'foo', '--debug'] ) - mock_init_logging.assert_called_with( - awscrt.io.LogLevel.Debug, - ) + mock_set_log_level.assert_called_with(logging.DEBUG) - @mock.patch('awscrt.io.set_log_level') - def test_no_debug_disables_crt_logging(self, mock_init_logging): + @mock.patch('awscrt.logging.set_log_level') + def test_no_debug_disables_crt_logging(self, mock_set_log_level): self.driver.main(['s3', 'list-objects', '--bucket', 'foo']) - mock_init_logging.assert_called_with( - awscrt.io.LogLevel.NoLogs, - ) + mock_set_log_level.assert_called_with(logging.NOTSET) def test_throw_error_if_both_args_specified(self): args = ['--cli-auto-prompt', '--no-cli-auto-prompt'] diff --git a/tests/unit/test_logger.py b/tests/unit/test_logger.py index a4efd17aa72f..59b6a7a97f98 100644 --- a/tests/unit/test_logger.py +++ b/tests/unit/test_logger.py @@ -12,8 +12,6 @@ # language governing permissions and limitations under the License. import logging -import awscrt.io - from awscli.logger import ( disable_crt_logging, enable_crt_logging, @@ -47,16 +45,12 @@ def test_can_remove_stream_handler(self): log = logging.getLogger('test_stream_logger') self.assertEqual(len(log.handlers), 0) - @mock.patch('awscrt.io.set_log_level') - def test_can_enable_crt_logging(self, mock_init_logging): + @mock.patch('awscrt.logging.set_log_level') + def test_can_enable_crt_logging(self, mock_set_log_level): enable_crt_logging() - mock_init_logging.assert_called_with( - awscrt.io.LogLevel.Debug, - ) + mock_set_log_level.assert_called_with(logging.DEBUG) - @mock.patch('awscrt.io.set_log_level') - def test_can_disable_crt_logging(self, mock_init_logging): + @mock.patch('awscrt.logging.set_log_level') + def test_can_disable_crt_logging(self, mock_set_log_level): disable_crt_logging() - mock_init_logging.assert_called_with( - awscrt.io.LogLevel.NoLogs, - ) + mock_set_log_level.assert_called_with(logging.NOTSET)