diff --git a/doc/source/users/index.rst b/doc/source/users/index.rst index 5ea3649ae..363dabab9 100644 --- a/doc/source/users/index.rst +++ b/doc/source/users/index.rst @@ -96,3 +96,4 @@ can be customized. identity_v3 resource service_filter + utils diff --git a/doc/source/users/utils.rst b/doc/source/users/utils.rst new file mode 100644 index 000000000..f69638e30 --- /dev/null +++ b/doc/source/users/utils.rst @@ -0,0 +1,4 @@ +Utilities +========= +.. automodule:: openstack.utils + :members: enable_logging diff --git a/examples/common.py b/examples/common.py index c3b5a88b4..68bcdceda 100755 --- a/examples/common.py +++ b/examples/common.py @@ -42,8 +42,8 @@ import traceback import uuid from openstack import user_preference +from openstack import utils -CONSOLE_MESSAGE_FORMAT = '%(levelname)s: %(name)s %(message)s' _logger = logging.getLogger(__name__) @@ -318,31 +318,9 @@ def option_parser(): return parser -def configure_logging(opts): - """Typical app logging setup - - Based on OSC/cliff - - """ - - root_logger = logging.getLogger('') - - # Always send higher-level messages to the console via stderr - console = logging.StreamHandler(sys.stderr) - formatter = logging.Formatter(CONSOLE_MESSAGE_FORMAT) - console.setFormatter(formatter) - root_logger.addHandler(console) - - if opts.debug: - root_logger.setLevel(logging.DEBUG) - else: - root_logger.setLevel(logging.WARNING) - return - - def setup(): opts = option_parser().parse_args() - configure_logging(opts) + utils.enable_logging(opts.debug) return opts diff --git a/openstack/tests/test_utils.py b/openstack/tests/test_utils.py index 4031d9c8e..59e4e0868 100644 --- a/openstack/tests/test_utils.py +++ b/openstack/tests/test_utils.py @@ -12,9 +12,50 @@ import unittest +import mock + from openstack import utils +class Test_enable_logging(unittest.TestCase): + + def _console_tests(self, fake_logging, level, debug): + the_logger = mock.MagicMock() + fake_logging.getLogger.return_value = the_logger + + utils.enable_logging(debug=debug) + + self.assertEqual(the_logger.addHandler.call_count, 1) + the_logger.setLevel.assert_called_with(level) + + def _file_tests(self, fake_logging, level, debug): + the_logger = mock.MagicMock() + fake_logging.getLogger.return_value = the_logger + fake_path = "fake/path.log" + + utils.enable_logging(debug=debug, path=fake_path) + + fake_logging.FileHandler.assert_called_with(fake_path) + self.assertEqual(the_logger.addHandler.call_count, 2) + the_logger.setLevel.assert_called_with(level) + + @mock.patch("openstack.utils.logging") + def test_debug_console(self, fake_logging): + self._console_tests(fake_logging, fake_logging.DEBUG, True) + + @mock.patch("openstack.utils.logging") + def test_warning_console(self, fake_logging): + self._console_tests(fake_logging, fake_logging.WARNING, False) + + @mock.patch("openstack.utils.logging") + def test_debug_file(self, fake_logging): + self._file_tests(fake_logging, fake_logging.DEBUG, True) + + @mock.patch("openstack.utils.logging") + def test_warning_file(self, fake_logging): + self._file_tests(fake_logging, fake_logging.WARNING, False) + + class Test_urljoin(unittest.TestCase): def test_strings(self): diff --git a/openstack/utils.py b/openstack/utils.py index ae7342e0b..367a53985 100644 --- a/openstack/utils.py +++ b/openstack/utils.py @@ -10,6 +10,44 @@ # License for the specific language governing permissions and limitations # under the License. +import logging +import sys + + +def enable_logging(debug=False, path=None): + """Enable logging of messages to sys.stderr + + This function is available for debugging purposes. If you wish to + log this package's message in your application, the standard library + ``logging`` package will receive these messages in any handlers you + create. + + :param bool debug: Set this to ``True`` to receive debug messages, + which includes HTTP requests and responses, + or ``False`` for warning messages. + :param str path: If a *path* is specified, logging output will + written to that file in addition to sys.stderr. + The path is passed to logging.FileHandler, + which will append messages the file (and create + it if needed). *Default: None* + + :rtype: None + """ + root_logger = logging.getLogger('') + formatter = logging.Formatter( + '%(asctime)s %(levelname)s: %(name)s %(message)s') + + console = logging.StreamHandler(sys.stderr) + console.setFormatter(formatter) + root_logger.addHandler(console) + + if path is not None: + file_handler = logging.FileHandler(path) + file_handler.setFormatter(formatter) + root_logger.addHandler(file_handler) + + root_logger.setLevel(logging.DEBUG if debug else logging.WARNING) + def urljoin(*args): """A custom version of urljoin that simply joins strings into a path.