diff --git a/force_bdss/bdss_application.py b/force_bdss/bdss_application.py
index 2cc751620af346bcad03a6db223f75ec4faaa29c..a822528477d75c8a36ca856b41a5c753d46106b2 100644
--- a/force_bdss/bdss_application.py
+++ b/force_bdss/bdss_application.py
@@ -12,6 +12,8 @@ from .factory_registry_plugin import FactoryRegistryPlugin
 from .core_evaluation_driver import CoreEvaluationDriver
 from .core_mco_driver import CoreMCODriver
 
+log = logging.getLogger(__name__)
+
 
 class BDSSApplication(Application):
     """Main application for the BDSS.
@@ -47,7 +49,7 @@ class BDSSApplication(Application):
         try:
             mgr.map(functools.partial(_import_extensions, plugins))
         except NoMatches:
-            logging.info("No extensions found")
+            log.info("No extensions found")
 
         super(BDSSApplication, self).__init__(plugins=plugins)
 
@@ -56,7 +58,7 @@ def _import_extensions(plugins, ext):
     """Service routine extracted for testing.
     Imports the extension in the plugins argument.
     """
-    logging.info("Found extension {}".format(ext.obj))
+    log.info("Found extension {}".format(ext.obj))
     plugins.append(ext.obj)
 
 
@@ -65,7 +67,8 @@ def _load_failure_callback(plugins, manager, entry_point, exception):
     Reports failure to load a module through stevedore, using the
     on_load_failure_callback option.
     """
-    logging.error(
+    log.error(
         "Unable to load plugin {}. Exception: {}. Message: {}".format(
-            entry_point, exception.__class__.__name__, exception)
+            entry_point, exception.__class__.__name__, exception),
+        exc_info=True,
     )
diff --git a/force_bdss/cli/force_bdss.py b/force_bdss/cli/force_bdss.py
index 48ce95353a49a5531c68bd09d4e441c7151179d7..40503ce5116ce15db3d00e9f85d726b10d028c3b 100644
--- a/force_bdss/cli/force_bdss.py
+++ b/force_bdss/cli/force_bdss.py
@@ -1,3 +1,4 @@
+import logging
 import click
 
 from ..bdss_application import BDSSApplication
@@ -10,12 +11,26 @@ push_exception_handler(reraise_exceptions=True)
 
 @click.command()
 @click.option("--evaluate", is_flag=True)
+@click.option("--logfile",
+              type=click.Path(exists=False),
+              help="If specified, the log filename. "
+                   " If unspecified, the log will be written to stdout.")
 @click.argument('workflow_filepath', type=click.Path(exists=True))
-def run(evaluate, workflow_filepath):
+def run(evaluate, logfile, workflow_filepath):
 
-    application = BDSSApplication(
-        evaluate=evaluate,
-        workflow_filepath=workflow_filepath
-    )
+    logging_config = {}
+    if logfile is not None:
+        logging_config["filename"] = logfile
 
-    application.run()
+    logging.basicConfig(**logging_config)
+    log = logging.getLogger(__name__)
+
+    try:
+        application = BDSSApplication(
+            evaluate=evaluate,
+            workflow_filepath=workflow_filepath
+        )
+
+        application.run()
+    except Exception as e:
+        log.exception(e)
diff --git a/force_bdss/cli/tests/test_execution.py b/force_bdss/cli/tests/test_execution.py
index 3fbf57fb179b41dea4232cacd0067a89562288f6..1311a92dad32fedd02449b4bf21f49b1b037fd1b 100644
--- a/force_bdss/cli/tests/test_execution.py
+++ b/force_bdss/cli/tests/test_execution.py
@@ -25,8 +25,10 @@ def fixture_dir():
 class TestExecution(unittest.TestCase):
     def test_plain_invocation_mco(self):
         with cd(fixtures.dirpath()):
-            out = subprocess.check_call(["force_bdss", "test_empty.json"])
-            self.assertEqual(out, 0)
+            try:
+                subprocess.check_output(["force_bdss", "test_empty.json"])
+            except subprocess.CalledProcessError:
+                self.fail("force_bdss returned error at plain invocation.")
 
     def test_plain_invocation_evaluate(self):
         with cd(fixtures.dirpath()):
@@ -41,13 +43,16 @@ class TestExecution(unittest.TestCase):
     def test_unsupported_file_input(self):
         with cd(fixtures.dirpath()):
             with self.assertRaises(subprocess.CalledProcessError):
-                subprocess.check_call(["force_bdss", "test_csv_v2.json"])
+                subprocess.check_output(
+                    ["force_bdss", "test_csv_v2.json"],
+                    stderr=subprocess.STDOUT)
 
     def test_corrupted_file_input(self):
         with cd(fixtures.dirpath()):
             with self.assertRaises(subprocess.CalledProcessError):
-                subprocess.check_call(["force_bdss",
-                                       "test_csv_corrupted.json"])
+                subprocess.check_output(
+                    ["force_bdss", "test_csv_corrupted.json"],
+                    stderr=subprocess.STDOUT)
 
 
 if __name__ == '__main__':
diff --git a/force_bdss/core_evaluation_driver.py b/force_bdss/core_evaluation_driver.py
index 33fc4755a456ad4c0609b0365c643e8f844641a5..1f4b9dc42b10b5d10d9e3719246628cd5ed1f88f 100644
--- a/force_bdss/core_evaluation_driver.py
+++ b/force_bdss/core_evaluation_driver.py
@@ -1,5 +1,3 @@
-from __future__ import print_function
-
 import sys
 import logging
 
@@ -14,6 +12,8 @@ from .io.workflow_reader import (
 
 CORE_EVALUATION_DRIVER_ID = plugin_id("core", "CoreEvaluationDriver")
 
+log = logging.getLogger(__name__)
+
 
 class CoreEvaluationDriver(BaseCoreDriver):
     """Main plugin that handles the execution of the MCO
@@ -26,12 +26,12 @@ class CoreEvaluationDriver(BaseCoreDriver):
         try:
             workflow = self.workflow
         except (InvalidVersionException, InvalidFileException) as e:
-            print(str(e), file=sys.stderr)
+            log.exception(e)
             sys.exit(1)
 
         mco_model = workflow.mco
         if mco_model is None:
-            print("No MCO defined. Nothing to do. Exiting.")
+            log.info("No MCO defined. Nothing to do. Exiting.")
             sys.exit(0)
 
         mco_factory = mco_model.factory
@@ -105,7 +105,7 @@ def _compute_layer_results(environment_data_values,
             in_slots)
 
         # execute data source, passing only relevant data values.
-        logging.info("Evaluating for Data Source {}".format(
+        log.info("Evaluating for Data Source {}".format(
             factory.name))
         res = evaluator.run(model, passed_data_values)
 
@@ -118,7 +118,7 @@ def _compute_layer_results(environment_data_values,
                 len(res), factory.name, len(out_slots)
             )
 
-            logging.error(error_txt)
+            log.error(error_txt)
             raise RuntimeError(error_txt)
 
         if len(res) != len(model.output_slot_names):
@@ -133,7 +133,7 @@ def _compute_layer_results(environment_data_values,
                 len(model.output_slot_names)
             )
 
-            logging.error(error_txt)
+            log.error(error_txt)
             raise RuntimeError(error_txt)
 
         # At this point, the returned data values are unnamed.
@@ -173,7 +173,7 @@ def _get_data_values_from_mco(model, communicator):
                      " file is corrupted.").format(
             len(mco_data_values), len(model.parameters)
         )
-        logging.error(error_txt)
+        log.error(error_txt)
         raise RuntimeError(error_txt)
 
     # The data values obtained by the communicator are unnamed.
diff --git a/force_bdss/core_mco_driver.py b/force_bdss/core_mco_driver.py
index 478499af138b50ee6ecdd1abe0ca95e583d37e85..ea2cbfafbec5683480450ec2854d402137d2b717 100644
--- a/force_bdss/core_mco_driver.py
+++ b/force_bdss/core_mco_driver.py
@@ -1,5 +1,3 @@
-from __future__ import print_function
-
 import sys
 import logging
 
@@ -44,12 +42,12 @@ class CoreMCODriver(BaseCoreDriver):
         try:
             workflow = self.workflow
         except (InvalidVersionException, InvalidFileException) as e:
-            print(str(e), file=sys.stderr)
+            log.exception(e)
             sys.exit(1)
 
         mco_model = workflow.mco
         if mco_model is None:
-            print("No MCO defined. Nothing to do. Exiting.")
+            log.info("No MCO defined. Nothing to do. Exiting.")
             sys.exit(0)
 
         mco_factory = mco_model.factory
diff --git a/force_bdss/io/tests/test_workflow_reader.py b/force_bdss/io/tests/test_workflow_reader.py
index 8ff216d88a04897742bcafbdd28094c40d1c54f8..c85263ae7520734131714bcebea9076fe2971900 100644
--- a/force_bdss/io/tests/test_workflow_reader.py
+++ b/force_bdss/io/tests/test_workflow_reader.py
@@ -2,6 +2,8 @@ import json
 import unittest
 from six import StringIO
 
+import testfixtures
+
 from force_bdss.factory_registry_plugin import FactoryRegistryPlugin
 from force_bdss.io.workflow_reader import (
     WorkflowReader,
@@ -29,15 +31,17 @@ class TestWorkflowReader(unittest.TestCase):
             "workflow": {}
         }
 
-        with self.assertRaises(InvalidVersionException):
-            self.wfreader.read(self._as_json_stringio(data))
+        with testfixtures.LogCapture():
+            with self.assertRaises(InvalidVersionException):
+                self.wfreader.read(self._as_json_stringio(data))
 
     def test_absent_version(self):
         data = {
         }
 
-        with self.assertRaises(InvalidFileException):
-            self.wfreader.read(self._as_json_stringio(data))
+        with testfixtures.LogCapture():
+            with self.assertRaises(InvalidFileException):
+                self.wfreader.read(self._as_json_stringio(data))
 
     def test_missing_key(self):
         data = {
@@ -45,8 +49,9 @@ class TestWorkflowReader(unittest.TestCase):
             "workflow": {}
         }
 
-        with self.assertRaises(InvalidFileException):
-            self.wfreader.read(self._as_json_stringio(data))
+        with testfixtures.LogCapture():
+            with self.assertRaises(InvalidFileException):
+                self.wfreader.read(self._as_json_stringio(data))
 
     def _as_json_stringio(self, data):
         fp = StringIO()
diff --git a/force_bdss/io/tests/test_workflow_writer.py b/force_bdss/io/tests/test_workflow_writer.py
index 7d649c067abf006eb9e71dd36feea441610fe1dd..74a8dbf90ff1b776f1bccbc9f40a73462a1d16cf 100644
--- a/force_bdss/io/tests/test_workflow_writer.py
+++ b/force_bdss/io/tests/test_workflow_writer.py
@@ -53,7 +53,6 @@ class TestWorkflowWriter(unittest.TestCase):
         fp = StringIO()
         wf = self._create_mock_workflow()
         wfwriter.write(wf, fp)
-        print(fp.getvalue())
         fp.seek(0)
         wfreader = WorkflowReader(self.mock_registry)
         wf_result = wfreader.read(fp)
diff --git a/force_bdss/io/workflow_reader.py b/force_bdss/io/workflow_reader.py
index 19d4ecbb9b06309cc1169d7cd465da32558543f2..3222733c555f34f27ddd63d155906cc878df44a9 100644
--- a/force_bdss/io/workflow_reader.py
+++ b/force_bdss/io/workflow_reader.py
@@ -7,6 +7,8 @@ from force_bdss.core.input_slot_map import InputSlotMap
 from force_bdss.core.workflow import Workflow
 from ..factory_registry_plugin import IFactoryRegistryPlugin
 
+log = logging.getLogger(__name__)
+
 SUPPORTED_FILE_VERSIONS = ["1"]
 
 
@@ -69,12 +71,12 @@ class WorkflowReader(HasStrictTraits):
         try:
             version = json_data["version"]
         except KeyError:
-            logging.error("File missing version information")
+            log.error("File missing version information")
             raise InvalidFileException("Corrupted input file, no version"
                                        " specified")
 
         if version not in SUPPORTED_FILE_VERSIONS:
-            logging.error(
+            log.error(
                 "File contains version {} that is not in the "
                 "list of supported versions {}".format(
                     version, SUPPORTED_FILE_VERSIONS)
@@ -92,7 +94,7 @@ class WorkflowReader(HasStrictTraits):
             wf.notification_listeners[:] = \
                 self._extract_notification_listeners(wf_data)
         except KeyError as e:
-            logging.exception("Could not read file {}".format(file))
+            log.error("Could not read file {}".format(file), exc_info=True)
             raise InvalidFileException("Could not read file {}. "
                                        "Unable to find key {}".format(file, e))
         return wf
diff --git a/force_bdss/tests/test_bdss_application.py b/force_bdss/tests/test_bdss_application.py
index abf417075b150dba73118863c254dcf3585a0714..53e5a5cf2e3c3b82f0d7260d7a47018c2d672c68 100644
--- a/force_bdss/tests/test_bdss_application.py
+++ b/force_bdss/tests/test_bdss_application.py
@@ -1,4 +1,6 @@
 import unittest
+import warnings
+
 import testfixtures
 
 from force_bdss.bdss_application import (
@@ -15,7 +17,9 @@ except ImportError:
 
 class TestBDSSApplication(unittest.TestCase):
     def test_initialization(self):
-        app = BDSSApplication(False, "foo/bar")
+        with warnings.catch_warnings():
+            warnings.simplefilter("ignore")
+            app = BDSSApplication(False, "foo/bar")
         self.assertFalse(app.evaluate)
         self.assertEqual(app.workflow_filepath, "foo/bar")
 
@@ -28,8 +32,10 @@ class TestBDSSApplication(unittest.TestCase):
                                    Exception("hello"))
 
         log.check(
-            ('root', 'ERROR', "Unable to load plugin foo. Exception: "
-                              "Exception. Message: hello")
+            ('force_bdss.bdss_application',
+             'ERROR',
+             "Unable to load plugin foo. Exception: "
+             "Exception. Message: hello"),
         )
         self.assertEqual(plugins, [])
 
diff --git a/force_bdss/tests/test_core_evaluation_driver.py b/force_bdss/tests/test_core_evaluation_driver.py
index 77058e5957621fcf6279d9a0e197f8203d5cae99..a6084e73294e123a6570e3a2086b08cdb948729d 100644
--- a/force_bdss/tests/test_core_evaluation_driver.py
+++ b/force_bdss/tests/test_core_evaluation_driver.py
@@ -1,5 +1,8 @@
 import unittest
 
+import testfixtures
+import six
+
 from force_bdss.tests.probe_classes.factory_registry_plugin import \
     ProbeFactoryRegistryPlugin
 from force_bdss.tests.probe_classes.mco import ProbeMCOFactory
@@ -46,10 +49,12 @@ class TestCoreEvaluationDriver(unittest.TestCase):
             nb_output_data_values=1)
         driver = CoreEvaluationDriver(
             application=self.mock_application)
-        with self.assertRaisesRegexp(
-                RuntimeError,
-                "The number of data values returned by the MCO"):
-            driver.application_started()
+        with testfixtures.LogCapture():
+            with six.assertRaisesRegex(
+                    self,
+                    RuntimeError,
+                    "The number of data values returned by the MCO"):
+                driver.application_started()
 
     def test_error_for_incorrect_output_slots(self):
         data_source_factories = \
@@ -62,12 +67,14 @@ class TestCoreEvaluationDriver(unittest.TestCase):
             run_function=run)
         driver = CoreEvaluationDriver(
             application=self.mock_application)
-        with self.assertRaisesRegexp(
-                RuntimeError,
-                "The number of data values \(1 values\)"
-                " returned by 'test_data_source' does not match"
-                " the number of output slots"):
-            driver.application_started()
+        with testfixtures.LogCapture():
+            with six.assertRaisesRegex(
+                    self,
+                    RuntimeError,
+                    "The number of data values \(1 values\)"
+                    " returned by 'test_data_source' does not match"
+                    " the number of output slots"):
+                driver.application_started()
 
     def test_error_for_missing_ds_output_names(self):
         data_source_factories = \
@@ -82,12 +89,14 @@ class TestCoreEvaluationDriver(unittest.TestCase):
         driver = CoreEvaluationDriver(
             application=self.mock_application,
         )
-        with self.assertRaisesRegexp(
-                RuntimeError,
-                "The number of data values \(1 values\)"
-                " returned by 'test_data_source' does not match"
-                " the number of user-defined names"):
-            driver.application_started()
+        with testfixtures.LogCapture():
+            with six.assertRaisesRegex(
+                    self,
+                    RuntimeError,
+                    "The number of data values \(1 values\)"
+                    " returned by 'test_data_source' does not match"
+                    " the number of user-defined names"):
+                driver.application_started()
 
     def test_error_for_incorrect_kpic_output_slots(self):
         kpi_calculator_factories = \
@@ -101,12 +110,15 @@ class TestCoreEvaluationDriver(unittest.TestCase):
         driver = CoreEvaluationDriver(
             application=self.mock_application,
         )
-        with self.assertRaisesRegexp(
-                RuntimeError,
-                "The number of data values \(1 values\)"
-                " returned by 'test_kpi_calculator' does not match"
-                " the number of output slots"):
-            driver.application_started()
+        with testfixtures.LogCapture():
+            with six.assertRaisesRegex(
+                    self,
+                    RuntimeError,
+                    "The number of data values \(1 values\)"
+                    " returned by 'test_kpi_calculator' does not match"
+                    " the number of output slots"):
+
+                driver.application_started()
 
     def test_error_for_missing_kpic_output_names(self):
         kpi_calculator_factories = \
@@ -121,12 +133,15 @@ class TestCoreEvaluationDriver(unittest.TestCase):
         driver = CoreEvaluationDriver(
             application=self.mock_application,
         )
-        with self.assertRaisesRegexp(
-                RuntimeError,
-                "The number of data values \(1 values\)"
-                " returned by 'test_kpi_calculator' does not match"
-                " the number of user-defined names"):
-            driver.application_started()
+
+        with testfixtures.LogCapture():
+            with six.assertRaisesRegex(
+                    self,
+                    RuntimeError,
+                    "The number of data values \(1 values\)"
+                    " returned by 'test_kpi_calculator' does not match"
+                    " the number of user-defined names"):
+                driver.application_started()
 
     def test_bind_data_values(self):
         data_values = [
@@ -154,11 +169,13 @@ class TestCoreEvaluationDriver(unittest.TestCase):
             InputSlotMap(name="baz"),
         )
 
-        with self.assertRaisesRegexp(
-                RuntimeError,
-                "The length of the slots is not equal to the length of"
-                " the slot map"):
-            _bind_data_values(data_values, slot_map, slots)
+        with testfixtures.LogCapture():
+            with six.assertRaisesRegex(
+                    self,
+                    RuntimeError,
+                    "The length of the slots is not equal to the length of"
+                    " the slot map"):
+                _bind_data_values(data_values, slot_map, slots)
 
         # missing value in the given data values.
         slot_map = (
@@ -166,11 +183,13 @@ class TestCoreEvaluationDriver(unittest.TestCase):
             InputSlotMap(name="bar")
         )
 
-        with self.assertRaisesRegexp(
-                RuntimeError,
-                "Unable to find requested name 'blap' in available"
-                " data values."):
-            _bind_data_values(data_values, slot_map, slots)
+        with testfixtures.LogCapture():
+            with six.assertRaisesRegex(
+                    self,
+                    RuntimeError,
+                    "Unable to find requested name 'blap' in available"
+                    " data values."):
+                _bind_data_values(data_values, slot_map, slots)
 
     def test_compute_layer_results(self):
         data_values = [
diff --git a/force_bdss/tests/test_factory_registry_plugin.py b/force_bdss/tests/test_factory_registry_plugin.py
index 3a90f7b144d7d05a64daa0ea9525719a6c5a0de4..fccc89aa42d3c019c0b619989d5fb4e421d8fcea 100644
--- a/force_bdss/tests/test_factory_registry_plugin.py
+++ b/force_bdss/tests/test_factory_registry_plugin.py
@@ -1,4 +1,5 @@
 import unittest
+import warnings
 
 from force_bdss.base_extension_plugin import (
     BaseExtensionPlugin)
@@ -70,10 +71,12 @@ class MySuperPlugin(BaseExtensionPlugin):
 
 class TestFactoryRegistryWithContent(unittest.TestCase):
     def setUp(self):
-        self.plugin = FactoryRegistryPlugin()
-        self.app = Application([self.plugin, MySuperPlugin()])
-        self.app.start()
-        self.app.stop()
+        with warnings.catch_warnings():
+            warnings.simplefilter("ignore")
+            self.plugin = FactoryRegistryPlugin()
+            self.app = Application([self.plugin, MySuperPlugin()])
+            self.app.start()
+            self.app.stop()
 
     def test_initialization(self):
         self.assertEqual(len(self.plugin.mco_factories), 1)