Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 38 additions & 6 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,19 +281,51 @@ def _add_upgrade_test(_item: Item, _upgrade_deployment_modes: list[str]) -> bool


def pytest_sessionstart(session: Session) -> None:
log_file = session.config.getoption("log_file") or "pytest-tests.log"
log_file = session.config.getoption(name="log_file") or "pytest-tests.log"
tests_log_file = os.path.join(get_base_dir(), log_file)
LOGGER.info(f"Writing tests log to {tests_log_file}")
thread_name = os.environ.get("PYTEST_XDIST_WORKER", "")

# Get log level and convert to integer if it's a string
log_cli_level = session.config.getoption(name="log_cli_level")
if log_cli_level and isinstance(log_cli_level, str):
log_level = getattr(logging, log_cli_level.upper(), logging.INFO)
else:
log_level = log_cli_level or logging.INFO

# Clean up old log file BEFORE setting up logging
if os.path.exists(tests_log_file):
pathlib.Path(tests_log_file).unlink()
if session.config.getoption("--collect-must-gather"):
session.config.option.must_gather_db = Database()
thread_name = os.environ.get("PYTEST_XDIST_WORKER", "")

# Default behavior: enable console unless explicitly disabled
enable_console_value = True # Default to console enabled

# Check for explicit -o log_cli option in command line overrides
try:
overrides = getattr(session.config.option, "override_ini", []) or []
log_cli_override = next((override for override in overrides if override.startswith("log_cli=")), None)

if log_cli_override:
value = log_cli_override.split("=", 1)[1].lower()
enable_console_value = value not in ("false", "0", "no", "off")

except Exception as e:
# If there's any issue with option detection, fall back to default behavior
LOGGER.error(f"Error detecting log_cli option: {e}")
enable_console_value = True
Comment thread
dbasunag marked this conversation as resolved.

# Setup logging before any other operations
session.config.option.log_listener = setup_logging(
log_file=tests_log_file,
log_level=session.config.getoption("log_cli_level") or logging.INFO,
log_level=log_level,
thread_name=thread_name,
enable_console=enable_console_value,
)

# Now safe to log after configuration is applied (only to file when console disabled)
LOGGER.info(f"Writing tests log to {tests_log_file}")

if session.config.getoption("--collect-must-gather"):
session.config.option.must_gather_db = Database()
must_gather_dict = set_must_gather_collector_values()
shutil.rmtree(
path=must_gather_dict["must_gather_base_directory"],
Expand Down
7 changes: 7 additions & 0 deletions docs/GETTING_STARTED.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,13 @@ uv run pytest -c custom-pytest.ini

```

### Turning off console logging
By default, pytest will output logging reports in the console. You can disable this behavior with `-o log_cli=false`

```bash
uv run pytest -o log_cli=false
```

### Running specific tests
```bash
uv run pytest -k test_name
Expand Down
41 changes: 33 additions & 8 deletions utilities/logger.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import logging
import os
import shutil
from logging.handlers import QueueHandler, QueueListener, RotatingFileHandler
import multiprocessing
from typing import Optional
from typing import Optional, Any

from simple_logger.logger import DuplicateFilter, WrapperLogFormatter

Expand All @@ -22,7 +23,7 @@ def __repr__(self) -> str:


def setup_logging(
log_level: int, log_file: str = "/tmp/pytest-tests.log", thread_name: str | None = None
log_level: int, log_file: str = "/tmp/pytest-tests.log", thread_name: str | None = None, enable_console: bool = True
) -> QueueListener:
"""
Setup basic/root logging using QueueHandler/QueueListener
Expand Down Expand Up @@ -61,15 +62,26 @@ def setup_logging(
secondary_log_colors={},
)

console_handler = logging.StreamHandler()
log_file_handler = RotatingFileHandler(filename=log_file, maxBytes=100 * 1024 * 1024, backupCount=20)
log_file_handler.setLevel(level=log_level) # Set the file handler log level

handlers: list[Any] = [log_file_handler]

# Only add console handler if explicitly enabled
# Also respect PYTEST_DISABLE_CONSOLE_LOG environment variable
disable_console = os.environ.get("PYTEST_DISABLE_CONSOLE_LOG", "").lower() in ("1", "true", "yes")
Comment thread
dbasunag marked this conversation as resolved.
Outdated

# Convert log_level to int if it's a string
if isinstance(log_level, str):
log_level = getattr(logging, log_level.upper(), logging.INFO)

if enable_console and not disable_console:
Comment thread
dbasunag marked this conversation as resolved.
Outdated
console_handler = logging.StreamHandler()
console_handler.setLevel(level=log_level) # Set the console handler log level
handlers.append(console_handler)

Comment thread
dbasunag marked this conversation as resolved.
log_queue = multiprocessing.Queue(maxsize=-1) # type: ignore[var-annotated]
log_listener = QueueListener(
log_queue,
log_file_handler,
console_handler,
)
log_listener = QueueListener(log_queue, *handlers)

basic_log_queue_handler = QueueHandler(queue=log_queue)
basic_log_queue_handler.set_name(name="basic")
Expand All @@ -93,12 +105,25 @@ def setup_logging(
root_logger.propagate = False
basic_logger.propagate = False

# Always configure all loggers to use our queue system
# This ensures test loggers and third-party loggers respect our console setting
for name, logger in logging.root.manager.loggerDict.items():
if isinstance(logger, logging.Logger) and (name not in ("root", "basic")):
logger.handlers.clear()
logger.addHandler(hdlr=root_log_queue_handler)
logger.propagate = False

# Configure the root logger to catch any new loggers that inherit from it
# First, completely clear any existing configuration
logging.root.handlers.clear()
logging.root.setLevel(level=log_level) # Set root logger to respect our log level
logging.root.addHandler(hdlr=root_log_queue_handler)

# Also ensure the root logger doesn't have any lingering configuration
for handler in logging.root.handlers[:]:
if handler != root_log_queue_handler:
logging.root.removeHandler(hdlr=handler)

log_listener.start()
return log_listener

Expand Down