diff --git a/src/ska/logging/transactions.py b/src/ska/logging/transactions.py
index 100430bd6b85df2ed826c82558219304f1f107c1..3eee962bc7fc9b0eee8e6d9200704c41a1df0a74 100644
--- a/src/ska/logging/transactions.py
+++ b/src/ska/logging/transactions.py
@@ -1,6 +1,9 @@
 # -*- coding: utf-8 -*-
 
 """This module provides the transaction logging mechanism."""
+import json
+import logging
+
 from typing import Mapping, Text
 
 
@@ -33,7 +36,11 @@ class transaction:
         self._params = params
 
     def __enter__(self):
-        return self._get_new_or_existing_transaction_id()
+        transaction_id = self._get_new_or_existing_transaction_id()
+        params_json = json.dumps(self._params)
+        # Question:  should we have a user provided logger instead of using root?
+        logging.info(f"Start transaction {self._name}, {transaction_id}, {params_json}")
+        return transaction_id
 
     def __exit__(self, exc_type, exc_val, exc_tb):
         pass
diff --git a/tests/conftest.py b/tests/conftest.py
new file mode 100644
index 0000000000000000000000000000000000000000..4c9967f00a223dc71ac69517d56339f18acab6cc
--- /dev/null
+++ b/tests/conftest.py
@@ -0,0 +1,122 @@
+"""
+A module defining pytest fixtures for testing ska.logging.
+"""
+import logging
+
+import pytest
+
+from ska.logging import configure_logging
+
+
+@pytest.fixture
+def reset_logging():
+    """Cleanup logging module's state after each test (at least try to)."""
+    yield
+    manager = logging.root.manager
+    manager.disabled = logging.NOTSET
+    for logger in list(manager.loggerDict.values()) + [logging.root]:
+        if isinstance(logger, logging.Logger):
+            logger.setLevel(logging.NOTSET)
+            logger.propagate = True
+            logger.disabled = False
+            logger.filters.clear()
+            handlers = logger.handlers.copy()
+            for handler in handlers:
+                # Copied from `logging.shutdown`.
+                try:
+                    handler.acquire()
+                    handler.flush()
+                    handler.close()
+                except (OSError, ValueError):
+                    pass
+                finally:
+                    handler.release()
+                logger.removeHandler(handler)
+
+
+class AppendHandler(logging.Handler):
+    """Handler that keeps a history of the records and formatted log messages."""
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.logs = []
+        self.records = []
+
+    def emit(self, record):
+        self.records.append(record)
+        self.logs.append(self.format(record))
+
+
+"""Override configuration for recording loggers"""
+RECORDER_OVERRIDES = {
+    "handlers": {"recorder": {"()": AppendHandler, "formatter": "default"}},
+    "root": {"handlers": ["console", "recorder"]},
+}
+
+
+@pytest.fixture
+def default_logger():
+    """Return user logger instance with default configuration."""
+    configure_logging()
+    yield logging.getLogger("ska.test.app")
+
+
+@pytest.fixture
+def recording_logger():
+    """Return user logger, including a recording handler.
+
+    The additional handler has the name "recorder".  It uses the default formatter,
+    and stores all formatted output strings as a list in its `logs` attribute.
+    It also keeps a list of the raw log records in its `records` attribute.
+
+    Note:  we use this instead of pytest's `caplog` fixture because we cannot change
+    the formatter that it uses.
+    """
+    configure_logging(overrides=RECORDER_OVERRIDES)
+    yield logging.getLogger("ska.logger")
+
+
+@pytest.fixture
+def recording_tags_logger():
+    """Return user logger like :func:`recording_logger`, but including tags filter."""
+
+    class MyFilter(logging.Filter):
+        def filter(self, record):
+            record.tags = "key1:value1,key2:value2"
+            return True
+
+    configure_logging(tags_filter=MyFilter, overrides=RECORDER_OVERRIDES)
+    yield logging.getLogger("ska.logger")
+
+
+def get_first_record_and_log_message(logger):
+    recorder = get_named_handler(logger, "recorder")
+    record = recorder.records[0]
+    log_message = recorder.logs[0]
+    return record, log_message
+
+
+def get_named_handler(logger, name):
+    """Search up through logger hierarchy to find a handler with the specified name."""
+    while logger:
+        for handler in logger.handlers:
+            if handler.name == name:
+                return handler
+        logger = logger.parent
+
+
+@pytest.fixture
+def id_provider_stub(mocker):
+    """Replace the standard transactions.IdProviderService with a simple stub implementation."""
+
+    class IdProviderStub:
+        last_id = "NOT SET"
+        call_count = 0
+
+        def generate_new_id(self):
+            IdProviderStub.last_id = "XYZ-789"
+            IdProviderStub.call_count += 1
+            return IdProviderStub.last_id
+
+    mocker.patch('ska.logging.transactions.IdProviderService', IdProviderStub)
+    yield IdProviderStub
diff --git a/tests/test_transactions.py b/tests/test_transactions.py
index 6b41f76f32feeef780858b59400b11d5eafc1793..05a31a7dfcd26b8c09aac0a331751c284acb4255 100644
--- a/tests/test_transactions.py
+++ b/tests/test_transactions.py
@@ -1,10 +1,13 @@
 # -*- coding: utf-8 -*-
 
 """Tests for the logging transactions module."""
+import json
+
 import pytest
 
 from ska.logging import transaction
 from ska.logging.transactions import TransactionParamsError
+from tests.conftest import get_first_record_and_log_message
 
 
 class TestTransactionIdGeneration:
@@ -65,6 +68,15 @@ class TestTransactionIdGeneration:
 class TestTransactionLogging:
     """Tests for :class:`~ska.logging.transaction` related to logging."""
 
+    def test_name_and_id_and_params_json_logged_on_entry(self, id_provider_stub, recording_logger):
+        params = {"other": ["config", 1, 2, 3.0]}
+        with transaction("name", params) as transaction_id:
+            pass
+        _, log_message = get_first_record_and_log_message(recording_logger)
+        assert "name" in log_message
+        assert transaction_id in log_message
+        assert json.dumps(params) in log_message
+
 
 class TestIdProviderService:
     """Tests for :class:`~ska.logging.transactions.IdProviderService`."""