More selective logging

Having the SDK configure the root logger is problematic as it
not only enables logging in the SDK but it also enables
logging in the users app and logging in other libs unrelated
to the SDK as an unintented side-effect. We should only log
for the openstack module.

We also want to be more selective about what stream to log
to so we provide the option to log to stdout or stderr.

Change-Id: I82ef867e182ebe4f29e21380268a3691e3b83494
This commit is contained in:
Everett Toews
2015-06-19 15:44:15 -05:00
parent 6f49e31e09
commit 0f5d3ec0e5
2 changed files with 44 additions and 19 deletions

View File

@@ -11,6 +11,7 @@
# under the License.
import mock
import sys
import testtools
from openstack import utils
@@ -18,11 +19,11 @@ from openstack import utils
class Test_enable_logging(testtools.TestCase):
def _console_tests(self, fake_logging, level, debug):
def _console_tests(self, fake_logging, level, debug, stream):
the_logger = mock.MagicMock()
fake_logging.getLogger.return_value = the_logger
utils.enable_logging(debug=debug)
utils.enable_logging(debug=debug, stream=stream)
self.assertEqual(the_logger.addHandler.call_count, 1)
the_logger.setLevel.assert_called_with(level)
@@ -35,16 +36,33 @@ class Test_enable_logging(testtools.TestCase):
utils.enable_logging(debug=debug, path=fake_path)
fake_logging.FileHandler.assert_called_with(fake_path)
self.assertEqual(the_logger.addHandler.call_count, 2)
self.assertEqual(the_logger.addHandler.call_count, 1)
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)
def test_none(self):
self.assertRaises(
ValueError, utils.enable_logging,
debug=True, path=None, stream=None)
@mock.patch("openstack.utils.logging")
def test_warning_console(self, fake_logging):
self._console_tests(fake_logging, fake_logging.WARNING, False)
def test_debug_console_stderr(self, fake_logging):
self._console_tests(fake_logging,
fake_logging.DEBUG, True, sys.stderr)
@mock.patch("openstack.utils.logging")
def test_warning_console_stderr(self, fake_logging):
self._console_tests(fake_logging,
fake_logging.WARNING, False, sys.stderr)
@mock.patch("openstack.utils.logging")
def test_debug_console_stdout(self, fake_logging):
self._console_tests(fake_logging,
fake_logging.DEBUG, True, sys.stdout)
@mock.patch("openstack.utils.logging")
def test_warning_console_stdout(self, fake_logging):
self._console_tests(fake_logging,
fake_logging.WARNING, False, sys.stdout)
@mock.patch("openstack.utils.logging")
def test_debug_file(self, fake_logging):

View File

@@ -11,11 +11,10 @@
# under the License.
import logging
import sys
def enable_logging(debug=False, path=None):
"""Enable logging of messages to sys.stderr
def enable_logging(debug=False, path=None, stream=None):
"""Enable logging to a file at path and/or a console stream.
This function is available for debugging purposes. If you wish to
log this package's message in your application, the standard library
@@ -26,27 +25,35 @@ def enable_logging(debug=False, path=None):
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.
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*
it if needed).
:param stream: One of ``None `` or ``sys.stdout`` or ``sys.stderr``.
If it is ``None``, nothing is logged to a stream.
If it isn't ``None``, console output is logged
to this stream.
:rtype: None
"""
root_logger = logging.getLogger('')
if path is None and stream is None:
raise ValueError("path and/or stream must be set")
logger = logging.getLogger('openstack')
formatter = logging.Formatter(
'%(asctime)s %(levelname)s: %(name)s %(message)s')
console = logging.StreamHandler(sys.stderr)
console.setFormatter(formatter)
root_logger.addHandler(console)
if stream is not None:
console = logging.StreamHandler(stream)
console.setFormatter(formatter)
logger.addHandler(console)
if path is not None:
file_handler = logging.FileHandler(path)
file_handler.setFormatter(formatter)
root_logger.addHandler(file_handler)
logger.addHandler(file_handler)
root_logger.setLevel(logging.DEBUG if debug else logging.WARNING)
logger.setLevel(logging.DEBUG if debug else logging.WARNING)
def urljoin(*args):