mirror of https://github.com/grpc/grpc.git
Added excessive logging threshold test (#37274)
* Added excessive logging threshold tests
* Excessive logs sample:
```
Warning: Excessive error output detected (7 lines):
WARNING: All log messages before absl::InitializeLog() is called are written to STDERR
I0000 00:00:1724852923.140229 45 chttp2_transport.cc:1182] ipv6:%5B::1%5D:45545: Got goaway [2] err=UNAVAILABLE:GOAWAY received; Error code: 2; Debug Text: Cancelling all calls {file:"src/core/ext/transport/chttp2/transport/chttp2_transport.cc", file_line:1171, created_time:"2024-08-28T13:48:43.140192497+00:00", http2_error:2, grpc_status:14}
```
Closes #37274
COPYBARA_INTEGRATE_REVIEW=https://github.com/grpc/grpc/pull/37274 from sourabhsinghs:unittest/logging-threshold 09a87693d0
PiperOrigin-RevId: 678742613
pull/37643/head^2
parent
72c7cf88f9
commit
dc12396125
6 changed files with 239 additions and 2 deletions
@ -0,0 +1,93 @@ |
||||
# Copyright 2024 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
import os |
||||
import re |
||||
import subprocess |
||||
import sys |
||||
import tempfile |
||||
|
||||
_OK_TEST_REGEX = r"^-+.*Ran ([\d]+) tests* in ([\d.]+)s.*OK(?: \(skipped=(\d+)\))?\n$" |
||||
|
||||
# Tests with known exception logs. |
||||
# TODO(sourabhsinghs): Investigate and enable _rpc_part_1_test and _rpc_part_2_test tests. |
||||
_SKIP_TESTS = [ |
||||
"_rpc_part_1_test", |
||||
"_server_shutdown_test", |
||||
"_xds_credentials_test", |
||||
"_server_test", |
||||
"_invalid_metadata_test", |
||||
"_reconnect_test", |
||||
"_channel_close_test", |
||||
"_rpc_part_2_test", |
||||
"_invocation_defects_test", |
||||
"_dynamic_stubs_test", |
||||
"_channel_connectivity_test", |
||||
] |
||||
|
||||
if __name__ == "__main__": |
||||
if len(sys.argv) != 3: |
||||
print(f"USAGE: {sys.argv[0]} TARGET_MODULE", file=sys.stderr) |
||||
sys.exit(1) |
||||
|
||||
test_script = sys.argv[1] |
||||
target_module = sys.argv[2] |
||||
|
||||
if target_module in _SKIP_TESTS: |
||||
print(f"Skipping {target_module}") |
||||
sys.exit(0) |
||||
|
||||
command = [ |
||||
sys.executable, |
||||
os.path.realpath(test_script), |
||||
target_module, |
||||
os.path.dirname(os.path.relpath(__file__)), |
||||
] |
||||
|
||||
with tempfile.TemporaryFile(mode="w+") as stdout_file: |
||||
with tempfile.TemporaryFile(mode="w+") as stderr_file: |
||||
result = subprocess.run( |
||||
command, |
||||
stdout=stdout_file, |
||||
stderr=stderr_file, |
||||
text=True, |
||||
check=True, |
||||
) |
||||
|
||||
stdout_file.seek(0) |
||||
stderr_file.seek(0) |
||||
|
||||
stdout_count = len(stdout_file.readlines()) |
||||
stderr_count = len(stderr_file.readlines()) |
||||
|
||||
if result.returncode != 0: |
||||
sys.exit("Test failure") |
||||
|
||||
stderr_file.seek(0) |
||||
if not re.fullmatch(_OK_TEST_REGEX, stderr_file.read(), re.DOTALL): |
||||
print( |
||||
f"Warning: Excessive error output detected ({stderr_count} lines):" |
||||
) |
||||
stderr_file.seek(0) |
||||
for line in stderr_file: |
||||
print(line) |
||||
|
||||
if stdout_count > 0: |
||||
print( |
||||
f"Warning: Unexpected output detected ({stdout_count} lines):" |
||||
) |
||||
stdout_file.seek(0) |
||||
for line in stdout_file: |
||||
print(line) |
||||
|
@ -0,0 +1,54 @@ |
||||
# Copyright 2024 gRPC authors. |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
|
||||
from typing import Sequence, Optional |
||||
|
||||
import unittest |
||||
import sys |
||||
import pkgutil |
||||
|
||||
|
||||
class SingleLoader(object): |
||||
def __init__(self, pattern: str, unittest_path: str): |
||||
loader = unittest.TestLoader() |
||||
self.suite = unittest.TestSuite() |
||||
tests = [] |
||||
|
||||
for importer, module_name, is_package in pkgutil.walk_packages([unittest_path]): |
||||
if pattern in module_name: |
||||
module = importer.find_module(module_name).load_module(module_name) |
||||
tests.append(loader.loadTestsFromModule(module)) |
||||
if len(tests) != 1: |
||||
raise AssertionError("Expected only 1 test module. Found {}".format(tests)) |
||||
self.suite.addTest(tests[0]) |
||||
|
||||
def loadTestsFromNames(self, names: Sequence[str], module: Optional[str] = None) -> unittest.TestSuite: |
||||
return self.suite |
||||
|
||||
if __name__ == "__main__": |
||||
|
||||
if len(sys.argv) != 3: |
||||
print(f"USAGE: {sys.argv[0]} TARGET_MODULE", file=sys.stderr) |
||||
sys.exit(1) |
||||
|
||||
|
||||
target_module = sys.argv[1] |
||||
unittest_path = sys.argv[2] |
||||
|
||||
loader = SingleLoader(target_module, unittest_path) |
||||
runner = unittest.TextTestRunner(verbosity=0) |
||||
result = runner.run(loader.suite) |
||||
|
||||
if not result.wasSuccessful(): |
||||
sys.exit("Test failure.") |
@ -0,0 +1,73 @@ |
||||
# Copyright 2024 The gRPC Authors |
||||
# |
||||
# Licensed under the Apache License, Version 2.0 (the "License"); |
||||
# you may not use this file except in compliance with the License. |
||||
# You may obtain a copy of the License at |
||||
# |
||||
# http://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, software |
||||
# distributed under the License is distributed on an "AS IS" BASIS, |
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
# See the License for the specific language governing permissions and |
||||
# limitations under the License. |
||||
""" |
||||
Houses py_grpc_logging_threshold_test. |
||||
""" |
||||
|
||||
_COPIED_MAIN_SUFFIX = ".logging_threshold.main" |
||||
|
||||
def py_grpc_logging_threshold_test( |
||||
name, |
||||
srcs, |
||||
main = None, |
||||
deps = None, |
||||
data = None, |
||||
**kwargs): |
||||
"""Runs a Python unit test and checks amount of logging against a threshold. |
||||
|
||||
Args: |
||||
name: The name of the test. |
||||
srcs: The source files. |
||||
main: The main file of the test. |
||||
deps: The dependencies of the test. |
||||
data: The data dependencies of the test. |
||||
**kwargs: Any other test arguments. |
||||
""" |
||||
if main == None: |
||||
if len(srcs) != 1: |
||||
fail("When main is not provided, srcs must be of size 1.") |
||||
main = srcs[0] |
||||
deps = [] if deps == None else deps |
||||
data = [] if data == None else data |
||||
|
||||
lib_name = name + ".logging_threshold.lib" |
||||
native.py_library( |
||||
name = lib_name, |
||||
srcs = srcs, |
||||
) |
||||
augmented_deps = deps + [ |
||||
":{}".format(lib_name), |
||||
] |
||||
|
||||
# The main file needs to be in the same package as the test file. |
||||
copied_main_name = name + _COPIED_MAIN_SUFFIX |
||||
copied_main_filename = copied_main_name + ".py" |
||||
native.genrule( |
||||
name = copied_main_name, |
||||
srcs = ["//bazel:_logging_threshold_test_main.py"], |
||||
outs = [copied_main_filename], |
||||
cmd = "cp $< $@", |
||||
) |
||||
|
||||
native.py_test( |
||||
name = name + ".logging_threshold", |
||||
args = ["$(location //bazel:_single_module_tester)", name], |
||||
data = data + ["//bazel:_single_module_tester"], |
||||
deps = augmented_deps, |
||||
srcs = [copied_main_filename], |
||||
main = copied_main_filename, |
||||
python_version = "PY3", |
||||
flaky = False, |
||||
**kwargs |
||||
) |
Loading…
Reference in new issue