diff --git a/mpsd-software-environment.py b/mpsd-software-environment.py
index 7bace592891f3395dedbbcdbc998f1c1ade38db5..50eefa1bf5990c4927f85b9f7b8b8eb04a2bfcc0 100755
--- a/mpsd-software-environment.py
+++ b/mpsd-software-environment.py
@@ -4,6 +4,7 @@
 
 import argparse
 import datetime
+import logging
 import os
 import subprocess
 import sys
@@ -11,6 +12,7 @@ import time
 from pathlib import Path
 from typing import List, Tuple
 
+
 about_tool = """
 Build toolchains using Spack.\n
 
@@ -30,6 +32,47 @@ config_vars = {
 }
 
 
+def set_up_logging(loglevel="warning", filename=None):
+    """Set up logging.
+
+    This function sets up the logging configuration for the script.
+    It configures the log level, log format, and log handlers
+    for both file and console output.
+
+
+    Parameters
+    ----------
+    loglevel : str or int
+       Loglevels are:
+         - warning (default): only print statements if something is unexpected
+         - info (show more detailed progress)
+         - debug (show very detailed output)
+    filename : str
+         - filename to save logging messages into
+
+    If loglevel is 'debug', save line numbers in log messages.
+    """
+    log_level_numeric = getattr(logging, loglevel.upper(), logging.WARNING)
+    assert log_level_numeric
+    if not isinstance(log_level_numeric, int):
+        raise ValueError("Invalid log level: %s" % loglevel)
+
+    handlers = []
+    if filename:
+        handlers.append(logging.FileHandler(filename))
+
+    handlers.append(logging.StreamHandler())
+    linenumbers = " %(lineno)4d" if log_level_numeric == logging.DEBUG else ""
+    logging.basicConfig(
+        format="%(asctime)s %(levelname)7s" + linenumbers + "  |  %(message)s",
+        datefmt="[%X]",
+        level=log_level_numeric,
+        handlers=handlers,
+        force=True,
+    )
+    logging.debug(f"Logging has been setup, loglevel={loglevel.upper()} {filename=}")
+
+
 # Helper class to change directory via context manager
 class os_chdir:
     """The os_chdir class is a context manager.
@@ -123,16 +166,16 @@ def run(*args, counter=[0], **kwargs):
     options = ", ".join([f"{key}={value}" for key, value in kwargs.items()])
 
     # provide information about upcoming subprocess.run call
-    print(f"{token} Starting subprocess.run({arg}) with options {options}")
-    print(f"{token}   getcwd={os.getcwd()}")
-    print(f"{token}   COMMAND={command}")
+    logging.info(f"{token} Starting subprocess.run('{command}') with options {options}")
+    logging.debug(f"{token}   getcwd={os.getcwd()}")
+    logging.debug(f"{token}   exact call: subprocess.run({arg})")
 
     time_start = time.time()
     process = subprocess.run(*args, **kwargs)
     execution_time = time.time() - time_start
 
-    print(f"{token}   Completed in {execution_time:.4f}s.")
-    print(f"{token}")  # near-empty line to make reading logs easier
+    logging.debug(f"{token}   Completed in {execution_time:.4f}s.")
+    logging.debug(f"{token}")  # near-empty line to make reading logs easier
     return process
 
 
@@ -395,7 +438,7 @@ def install_environment(
     -------
     None
     """
-    print(
+    logging.info(
         f"Installing release {mpsd_release} with toolchains {toolchains} "
         f"to {script_dir}"
     )
@@ -419,7 +462,7 @@ def install_environment(
     elif toolchains == "NONE":
         # No toolchains requested, so we only create the env and print the
         # list of available toolchains
-        print(
+        logging.warning(
             "No toolchains requested. Available toolchains for release "
             f"{mpsd_release} are: \n {available_toolchains}"
         )
@@ -443,7 +486,7 @@ def install_environment(
             # and replace _toolchains_ with the toolchain name and
             # _mpsd_spack_ver_ with mpsd_release
 
-            print(f"Installing toolchain {toolchain} to {toolchain_dir}")
+            logging.info(f"Installing toolchain {toolchain} to {toolchain_dir}")
             install_log_file = (
                 config_vars["build_log_file"]
                 .replace("mpsd_spack_ver_", f"{mpsd_release}_")
@@ -474,20 +517,30 @@ def install_environment(
 def remove_environment(release, toolchains, target_dir):
     """Remove release from installation."""
     msg = f"Removing release {release} with toolchains {toolchains} from {target_dir}"
-    print(msg)
+    logging.info(msg)
     raise NotImplementedError(msg)
 
 
 def start_new_environment(release, from_release, target_dir):
     """Start new MPSD software environment version."""
     msg = f"Starting new release {release} from {from_release} to {target_dir}"
-    print(msg)
+    logging.info(msg)
     raise NotImplementedError(msg)
 
 
 def main():
     """Execute main entry point."""
     parser = argparse.ArgumentParser(description=about_tool)
+    parser.add_argument(
+        "--log",
+        "-l",
+        dest="loglevel",
+        choices=["warning", "info", "debug"],
+        required=False,
+        default="warning",
+        help="Set the log level",
+    )
+
     subparsers = parser.add_subparsers(
         dest="action", title="actions", description="valid actions", required=True
     )
@@ -556,6 +609,9 @@ def main():
     # Carry out the action
     args = parser.parse_args()
 
+    # parse logging first
+    set_up_logging(args.loglevel)
+
     # target dir is the place where this script exists. the
     # release `dev` in script_dir/dev-23a
     script_dir = Path(os.path.dirname(os.path.realpath(__file__)))
diff --git a/tests.py b/tests.py
index 43d08a7be8cc80320ff7006f4718eda7eb4d8268..01bd4daeecfb3d9bf582ee53bbe1e477f6d5194f 100644
--- a/tests.py
+++ b/tests.py
@@ -1,6 +1,7 @@
 """Tests for mpsd-software-environment.py."""
 
 import importlib
+import logging
 import os
 import shutil
 import subprocess
@@ -10,6 +11,11 @@ import pytest
 
 mod = importlib.import_module("mpsd-software-environment")
 
+# set loglevel to debug - useful for understanding problems.
+# (if the tests pass, pytest doesn't show any output)
+mod.set_up_logging(loglevel="debug", filename="tests.log")
+logging.debug(f"We have set up logging from {__file__}")
+
 
 def create_mock_git_repository(target_directory, create_directory=True):
     """