mirror of
https://github.com/scylladb/scylladb.git
synced 2026-04-21 00:50:35 +00:00
Copy wait_for_any_log() function from dtest tools/log_utils.py with few modifications: - Add type hints; - Change timeout for node.watch_log_for() calls from 0 to 0.1 because dtest shim's implementation uses asyncio.timeout() and 0 means not "one time" but "never run"; - Use set() instead of list() for `ret` variable; - Remove redundant `found` variable. - Remove `remaining` variable and use shallow copies to make the code more correct. As a side effect this makes the TimeoutError message more correct too; - Use f-string formatting for TimeoutError message;
84 lines
2.8 KiB
Python
84 lines
2.8 KiB
Python
#
|
|
# Copyright (C) 2025-present ScyllaDB
|
|
#
|
|
# SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
|
|
#
|
|
|
|
from __future__ import annotations
|
|
|
|
import itertools
|
|
import logging
|
|
import re
|
|
import time
|
|
from datetime import datetime
|
|
from typing import TYPE_CHECKING
|
|
|
|
if TYPE_CHECKING:
|
|
from test.cluster.dtest.ccmlib.scylla_node import ScyllaNode
|
|
|
|
|
|
control_chars = "".join(map(chr, itertools.chain(range(0x20), range(0x7F, 0xA0))))
|
|
control_char_re = re.compile("[%s]" % re.escape(control_chars))
|
|
|
|
|
|
def remove_control_chars(s):
|
|
return control_char_re.sub("", s)
|
|
|
|
|
|
class DisableLogger:
|
|
def __init__(self, logger_name):
|
|
self.logger_name = logger_name
|
|
self.level = 0
|
|
|
|
def __enter__(self):
|
|
self.level = logging.getLogger(self.logger_name).level
|
|
logging.getLogger(self.logger_name).setLevel(logging.WARNING)
|
|
return self
|
|
|
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
logging.getLogger(self.logger_name).setLevel(self.level)
|
|
|
|
|
|
def wait_for_any_log(nodes: list[ScyllaNode],
|
|
patterns: str | list[str],
|
|
timeout: int,
|
|
dispersed: bool = False) -> ScyllaNode | list[ScyllaNode]:
|
|
"""Look for a pattern in the system.log of any in a given list of nodes.
|
|
|
|
:param nodes: The list of nodes whose logs to scan
|
|
:param patterns: The target pattern (a string, or a list of strings)
|
|
:param timeout: How long to wait for the pattern. Note that
|
|
strictly speaking, timeout is not really a timeout,
|
|
but a maximum number of attempts. This implies that
|
|
the all the grepping takes no time at all, so it is
|
|
somewhat inaccurate, but probably close enough.
|
|
:return: The first node in whose log the pattern was found, if not dispersed.
|
|
Otherwise, if dispersed=True, return a list of all nodes with any of the patterns.
|
|
"""
|
|
if dispersed:
|
|
patterns = patterns.copy()
|
|
ret = set()
|
|
for _ in range(timeout):
|
|
for node in nodes:
|
|
for p in patterns.copy():
|
|
try:
|
|
if node.watch_log_for(p, timeout=0.1):
|
|
patterns.remove(p)
|
|
ret.add(node)
|
|
except TimeoutError:
|
|
pass
|
|
if not patterns:
|
|
return list(ret)
|
|
time.sleep(1)
|
|
else:
|
|
for _ in range(timeout):
|
|
for node in nodes:
|
|
try:
|
|
if node.watch_log_for(patterns, timeout=0.1):
|
|
return node
|
|
except TimeoutError:
|
|
pass
|
|
time.sleep(1)
|
|
|
|
raise TimeoutError(f"{datetime.now():%d %b %Y %H:%M:%S} Unable to find :{patterns} in any node log within {timeout}s")
|