Skip to content

Commit ae6dec0

Browse files
authored
Support -o log_cli=<true/false> to turn off console logging (#884)
* Support -o log_cli=<true/false> to turn off console logging * Add information on how to disable console logging * Removed support for PYTEST_DISABLE_CONSOLE_LOG as not necessary
1 parent e2ae95a commit ae6dec0

File tree

3 files changed

+73
-14
lines changed

3 files changed

+73
-14
lines changed

conftest.py

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -281,19 +281,51 @@ def _add_upgrade_test(_item: Item, _upgrade_deployment_modes: list[str]) -> bool
281281

282282

283283
def pytest_sessionstart(session: Session) -> None:
284-
log_file = session.config.getoption("log_file") or "pytest-tests.log"
284+
log_file = session.config.getoption(name="log_file") or "pytest-tests.log"
285285
tests_log_file = os.path.join(get_base_dir(), log_file)
286-
LOGGER.info(f"Writing tests log to {tests_log_file}")
286+
thread_name = os.environ.get("PYTEST_XDIST_WORKER", "")
287+
288+
# Get log level and convert to integer if it's a string
289+
log_cli_level = session.config.getoption(name="log_cli_level")
290+
if log_cli_level and isinstance(log_cli_level, str):
291+
log_level = getattr(logging, log_cli_level.upper(), logging.INFO)
292+
else:
293+
log_level = log_cli_level or logging.INFO
294+
295+
# Clean up old log file BEFORE setting up logging
287296
if os.path.exists(tests_log_file):
288297
pathlib.Path(tests_log_file).unlink()
289-
if session.config.getoption("--collect-must-gather"):
290-
session.config.option.must_gather_db = Database()
291-
thread_name = os.environ.get("PYTEST_XDIST_WORKER", "")
298+
299+
# Default behavior: enable console unless explicitly disabled
300+
enable_console_value = True # Default to console enabled
301+
302+
# Check for explicit -o log_cli option in command line overrides
303+
try:
304+
overrides = getattr(session.config.option, "override_ini", []) or []
305+
log_cli_override = next((override for override in overrides if override.startswith("log_cli=")), None)
306+
307+
if log_cli_override:
308+
value = log_cli_override.split("=", 1)[1].lower()
309+
enable_console_value = value not in ("false", "0", "no", "off")
310+
311+
except Exception as e:
312+
# If there's any issue with option detection, fall back to default behavior
313+
LOGGER.error(f"Error detecting log_cli option: {e}")
314+
enable_console_value = True
315+
316+
# Setup logging before any other operations
292317
session.config.option.log_listener = setup_logging(
293318
log_file=tests_log_file,
294-
log_level=session.config.getoption("log_cli_level") or logging.INFO,
319+
log_level=log_level,
295320
thread_name=thread_name,
321+
enable_console=enable_console_value,
296322
)
323+
324+
# Now safe to log after configuration is applied (only to file when console disabled)
325+
LOGGER.info(f"Writing tests log to {tests_log_file}")
326+
327+
if session.config.getoption("--collect-must-gather"):
328+
session.config.option.must_gather_db = Database()
297329
must_gather_dict = set_must_gather_collector_values()
298330
shutil.rmtree(
299331
path=must_gather_dict["must_gather_base_directory"],

docs/GETTING_STARTED.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,13 @@ uv run pytest -c custom-pytest.ini
9494

9595
```
9696

97+
### Turning off console logging
98+
By default, pytest will output logging reports in the console. You can disable this behavior with `-o log_cli=false`
99+
100+
```bash
101+
uv run pytest -o log_cli=false
102+
```
103+
97104
### Running specific tests
98105
```bash
99106
uv run pytest -k test_name

utilities/logger.py

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import shutil
33
from logging.handlers import QueueHandler, QueueListener, RotatingFileHandler
44
import multiprocessing
5-
from typing import Optional
5+
from typing import Optional, Any
66

77
from simple_logger.logger import DuplicateFilter, WrapperLogFormatter
88

@@ -22,7 +22,7 @@ def __repr__(self) -> str:
2222

2323

2424
def setup_logging(
25-
log_level: int, log_file: str = "/tmp/pytest-tests.log", thread_name: str | None = None
25+
log_level: int, log_file: str = "/tmp/pytest-tests.log", thread_name: str | None = None, enable_console: bool = True
2626
) -> QueueListener:
2727
"""
2828
Setup basic/root logging using QueueHandler/QueueListener
@@ -61,15 +61,22 @@ def setup_logging(
6161
secondary_log_colors={},
6262
)
6363

64-
console_handler = logging.StreamHandler()
6564
log_file_handler = RotatingFileHandler(filename=log_file, maxBytes=100 * 1024 * 1024, backupCount=20)
65+
log_file_handler.setLevel(level=log_level) # Set the file handler log level
66+
67+
handlers: list[Any] = [log_file_handler]
68+
69+
# Convert log_level to int if it's a string
70+
if isinstance(log_level, str):
71+
log_level = getattr(logging, log_level.upper(), logging.INFO)
72+
73+
if enable_console:
74+
console_handler = logging.StreamHandler()
75+
console_handler.setLevel(level=log_level) # Set the console handler log level
76+
handlers.append(console_handler)
6677

6778
log_queue = multiprocessing.Queue(maxsize=-1) # type: ignore[var-annotated]
68-
log_listener = QueueListener(
69-
log_queue,
70-
log_file_handler,
71-
console_handler,
72-
)
79+
log_listener = QueueListener(log_queue, *handlers)
7380

7481
basic_log_queue_handler = QueueHandler(queue=log_queue)
7582
basic_log_queue_handler.set_name(name="basic")
@@ -93,12 +100,25 @@ def setup_logging(
93100
root_logger.propagate = False
94101
basic_logger.propagate = False
95102

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

111+
# Configure the root logger to catch any new loggers that inherit from it
112+
# First, completely clear any existing configuration
113+
logging.root.handlers.clear()
114+
logging.root.setLevel(level=log_level) # Set root logger to respect our log level
115+
logging.root.addHandler(hdlr=root_log_queue_handler)
116+
117+
# Also ensure the root logger doesn't have any lingering configuration
118+
for handler in logging.root.handlers[:]:
119+
if handler != root_log_queue_handler:
120+
logging.root.removeHandler(hdlr=handler)
121+
102122
log_listener.start()
103123
return log_listener
104124

0 commit comments

Comments
 (0)