Skip to content

LoggingPlugin

LoggingPlugin

LoggingPlugin(verifier)

Bases: BasePlugin

Logging interception plugin.

Patches logging.Logger._log globally. Uses reference counting so nested sandboxes work correctly, following the SubprocessPlugin pattern.

Source code in src/tripwire/plugins/logging_plugin.py
def __init__(self, verifier: "StrictVerifier") -> None:
    super().__init__(verifier)
    # FIFO queue for log mocks (per-plugin instance, per-verifier)
    self._mock_queue: deque[LogMockConfig] = deque()
    self._mock_consumed: list[LogMockConfig] = []
    self._sentinel = LogSentinel(self)

log property

log

Sentinel used as source argument in assert_interaction() for logging.

install

install()

No-op. Called to ensure plugin is registered before sandbox entry.

Access to any attribute of log triggers plugin creation via _LoggingProxy.getattr. This method exists as a named no-op so tests that want the interceptor active without any mocks have an explicit API to call.

Source code in src/tripwire/plugins/logging_plugin.py
def install(self) -> None:
    """No-op. Called to ensure plugin is registered before sandbox entry.

    Access to any attribute of log triggers plugin creation via
    _LoggingProxy.__getattr__. This method exists as a named no-op so
    tests that want the interceptor active without any mocks have an
    explicit API to call.
    """

mock_log

mock_log(level, message, logger_name=None, *, required=True)

Register a FIFO log mock.

Calls are matched in registration order. Unlike subprocess.run, unmocked log calls are swallowed (fire-and-forget) and recorded on the timeline, requiring assertion at teardown.

Source code in src/tripwire/plugins/logging_plugin.py
def mock_log(
    self,
    level: str,
    message: str,
    logger_name: str | None = None,
    *,
    required: bool = True,
) -> None:
    """Register a FIFO log mock.

    Calls are matched in registration order. Unlike subprocess.run,
    unmocked log calls are swallowed (fire-and-forget) and recorded
    on the timeline, requiring assertion at teardown.
    """
    self._mock_queue.append(
        LogMockConfig(
            level=level.upper(),
            message=message,
            logger_name=logger_name,
            required=required,
        )
    )

assert_log

assert_log(level, message, logger_name)

Assert the next log interaction with all 3 fields.

Source code in src/tripwire/plugins/logging_plugin.py
def assert_log(
    self,
    level: str,
    message: str,
    logger_name: str,
) -> None:
    """Assert the next log interaction with all 3 fields."""
    self.verifier.assert_interaction(
        self._sentinel,
        level=level.upper(),
        message=message,
        logger_name=logger_name,
    )

assert_debug

assert_debug(message, logger_name)

Assert the next log interaction is a DEBUG message.

Source code in src/tripwire/plugins/logging_plugin.py
def assert_debug(self, message: str, logger_name: str) -> None:
    """Assert the next log interaction is a DEBUG message."""
    self.assert_log("DEBUG", message, logger_name)

assert_info

assert_info(message, logger_name)

Assert the next log interaction is an INFO message.

Source code in src/tripwire/plugins/logging_plugin.py
def assert_info(self, message: str, logger_name: str) -> None:
    """Assert the next log interaction is an INFO message."""
    self.assert_log("INFO", message, logger_name)

assert_warning

assert_warning(message, logger_name)

Assert the next log interaction is a WARNING message.

Source code in src/tripwire/plugins/logging_plugin.py
def assert_warning(self, message: str, logger_name: str) -> None:
    """Assert the next log interaction is a WARNING message."""
    self.assert_log("WARNING", message, logger_name)

assert_error

assert_error(message, logger_name)

Assert the next log interaction is an ERROR message.

Source code in src/tripwire/plugins/logging_plugin.py
def assert_error(self, message: str, logger_name: str) -> None:
    """Assert the next log interaction is an ERROR message."""
    self.assert_log("ERROR", message, logger_name)

assert_critical

assert_critical(message, logger_name)

Assert the next log interaction is a CRITICAL message.

Source code in src/tripwire/plugins/logging_plugin.py
def assert_critical(self, message: str, logger_name: str) -> None:
    """Assert the next log interaction is a CRITICAL message."""
    self.assert_log("CRITICAL", message, logger_name)

check_conflicts

check_conflicts()

Verify logging.Logger._log has not been patched by a third party.

Source code in src/tripwire/plugins/logging_plugin.py
def check_conflicts(self) -> None:
    """Verify logging.Logger._log has not been patched by a third party."""
    from tripwire._errors import ConflictError

    current_log = logging.Logger._log
    if (
        current_log is not _LOGGER_LOG_ORIGINAL
        and current_log is not _tripwire_logger_log
    ):
        patcher = _identify_logging_patcher(current_log)
        raise ConflictError(
            target="logging.Logger._log",
            patcher=patcher,
        )