Implementation:MarketSquare Robotframework browser Thread Safe Logger
| Knowledge Sources | |
|---|---|
| Domains | Logging, Concurrency |
| Last Updated | 2026-02-12 05:40 GMT |
Overview
Thread-aware logging wrapper around Robot Framework's logger that buffers log messages during asynchronous execution and flushes them back to the main thread when safe.
Description
The Thread Safe Logger module provides a set of logging functions (info, debug, trace, warn, error, console, write) that mirror the Robot Framework logger API. A decorator called _stashing_logger wraps each log-level function so that when the current thread has been registered for stashing (via stash_this_thread), log messages are captured as deferred callables in a per-thread stash list (_THREAD_STASHES) rather than being emitted immediately. This prevents Robot Framework logging errors that occur when log calls are made from non-main threads during async gRPC communication.
The stash system supports nested stashing via a list-of-lists structure per thread. Calling stash_this_thread multiple times on the same thread pushes new inner stash layers. flush_and_delete_thread_stash either replays all stashed calls when on the outermost layer, or merges the current layer into the parent layer when nested. clear_thread_stash discards buffered messages from the current innermost stash without executing them.
Usage
Use this module as a drop-in replacement for direct robot.api.logger calls throughout the Browser library's Python codebase. Before entering an async or threaded section, call stash_this_thread to begin buffering. After the async work completes and control returns to the main Robot Framework execution context, call flush_and_delete_thread_stash to replay all buffered log messages in order. Call clear_thread_stash to discard stashed messages when error recovery discards the associated work.
Code Reference
Source Location
- Repository: MarketSquare_Robotframework_browser
- File: Browser/utils/logger.py
- Lines: 1-104
Signature
def _stashing_logger(funk: Callable) -> Callable
def info(msg: Any, html=False) -> None
def debug(msg: Any, html=False) -> None
def trace(msg: Any, html=False) -> None
def warn(msg: Any, html=False) -> None
def error(msg: Any, html=False) -> None
def console(msg: Any) -> None
def write(msg: Any, loglevel: LOGLEVEL, html=False) -> None
def stash_this_thread() -> None
def clear_thread_stash() -> None
def flush_and_delete_thread_stash() -> None
Import
from Browser.utils.logger import info, debug, trace, warn, error, console, write
from Browser.utils.logger import stash_this_thread, clear_thread_stash, flush_and_delete_thread_stash
I/O Contract
| Function | Input | Output | Description |
|---|---|---|---|
| info | msg: Any, html: bool | None | Logs at INFO level; stashes if current thread is registered |
| debug | msg: Any, html: bool | None | Logs at DEBUG level; stashes if current thread is registered |
| trace | msg: Any, html: bool | None | Logs at TRACE level; stashes if current thread is registered |
| warn | msg: Any, html: bool | None | Logs at WARN level; stashes if current thread is registered |
| error | msg: Any, html: bool | None | Logs at ERROR level; stashes if current thread is registered |
| console | msg: Any | None | Logs to console directly (not stashed) |
| write | msg: Any, loglevel: LOGLEVEL, html: bool | None | Dispatches to the appropriate log-level function based on loglevel string |
| stash_this_thread | None | None | Registers the current thread for log stashing; pushes a new stash layer |
| clear_thread_stash | None | None | Clears the innermost stash layer for the current thread without executing |
| flush_and_delete_thread_stash | None | None | Replays stashed log calls (outermost layer) or merges into parent layer (nested) |
Usage Examples
from Browser.utils.logger import info, stash_this_thread, flush_and_delete_thread_stash
# In an async/threaded context:
stash_this_thread()
try:
info("This message is buffered, not emitted immediately")
info("Another buffered message")
finally:
# Back on the main thread / safe context:
flush_and_delete_thread_stash()
# Both messages are now replayed in order through robot.api.logger