| # Copyright 2017 The Abseil 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. |
| |
| """Functional tests for absl.logging.""" |
| |
| import fnmatch |
| import os |
| import re |
| import shutil |
| import subprocess |
| import sys |
| import tempfile |
| |
| from absl import logging |
| from absl.testing import _bazelize_command |
| from absl.testing import absltest |
| from absl.testing import parameterized |
| |
| |
| _PY_VLOG3_LOG_MESSAGE = """\ |
| I1231 23:59:59.000000 12345 logging_functional_test_helper.py:62] This line is VLOG level 3 |
| """ |
| |
| _PY_VLOG2_LOG_MESSAGE = """\ |
| I1231 23:59:59.000000 12345 logging_functional_test_helper.py:64] This line is VLOG level 2 |
| I1231 23:59:59.000000 12345 logging_functional_test_helper.py:64] This line is log level 2 |
| I1231 23:59:59.000000 12345 logging_functional_test_helper.py:64] VLOG level 1, but only if VLOG level 2 is active |
| """ |
| |
| # VLOG1 is the same as DEBUG logs. |
| _PY_DEBUG_LOG_MESSAGE = """\ |
| I1231 23:59:59.000000 12345 logging_functional_test_helper.py:65] This line is VLOG level 1 |
| I1231 23:59:59.000000 12345 logging_functional_test_helper.py:65] This line is log level 1 |
| I1231 23:59:59.000000 12345 logging_functional_test_helper.py:66] This line is DEBUG |
| """ |
| |
| _PY_INFO_LOG_MESSAGE = """\ |
| I1231 23:59:59.000000 12345 logging_functional_test_helper.py:65] This line is VLOG level 0 |
| I1231 23:59:59.000000 12345 logging_functional_test_helper.py:65] This line is log level 0 |
| I1231 23:59:59.000000 12345 logging_functional_test_helper.py:70] Interesting Stuff\0 |
| I1231 23:59:59.000000 12345 logging_functional_test_helper.py:71] Interesting Stuff with Arguments: 42 |
| I1231 23:59:59.000000 12345 logging_functional_test_helper.py:73] Interesting Stuff with Dictionary |
| I1231 23:59:59.000000 12345 logging_functional_test_helper.py:123] This should appear 5 times. |
| I1231 23:59:59.000000 12345 logging_functional_test_helper.py:123] This should appear 5 times. |
| I1231 23:59:59.000000 12345 logging_functional_test_helper.py:123] This should appear 5 times. |
| I1231 23:59:59.000000 12345 logging_functional_test_helper.py:123] This should appear 5 times. |
| I1231 23:59:59.000000 12345 logging_functional_test_helper.py:123] This should appear 5 times. |
| I1231 23:59:59.000000 12345 logging_functional_test_helper.py:76] Info first 1 of 2 |
| I1231 23:59:59.000000 12345 logging_functional_test_helper.py:77] Info 1 (every 3) |
| I1231 23:59:59.000000 12345 logging_functional_test_helper.py:76] Info first 2 of 2 |
| I1231 23:59:59.000000 12345 logging_functional_test_helper.py:77] Info 4 (every 3) |
| """ |
| |
| _PY_INFO_LOG_MESSAGE_NOPREFIX = """\ |
| This line is VLOG level 0 |
| This line is log level 0 |
| Interesting Stuff\0 |
| Interesting Stuff with Arguments: 42 |
| Interesting Stuff with Dictionary |
| This should appear 5 times. |
| This should appear 5 times. |
| This should appear 5 times. |
| This should appear 5 times. |
| This should appear 5 times. |
| Info first 1 of 2 |
| Info 1 (every 3) |
| Info first 2 of 2 |
| Info 4 (every 3) |
| """ |
| |
| _PY_WARNING_LOG_MESSAGE = """\ |
| W1231 23:59:59.000000 12345 logging_functional_test_helper.py:65] This line is VLOG level -1 |
| W1231 23:59:59.000000 12345 logging_functional_test_helper.py:65] This line is log level -1 |
| W1231 23:59:59.000000 12345 logging_functional_test_helper.py:79] Worrying Stuff |
| W0000 23:59:59.000000 12345 logging_functional_test_helper.py:81] Warn first 1 of 2 |
| W0000 23:59:59.000000 12345 logging_functional_test_helper.py:82] Warn 1 (every 3) |
| W0000 23:59:59.000000 12345 logging_functional_test_helper.py:81] Warn first 2 of 2 |
| W0000 23:59:59.000000 12345 logging_functional_test_helper.py:82] Warn 4 (every 3) |
| """ |
| |
| if sys.version_info[0:2] == (3, 4): |
| _FAKE_ERROR_EXTRA_MESSAGE = """\ |
| Traceback (most recent call last): |
| File "logging_functional_test_helper.py", line 456, in _test_do_logging |
| raise OSError('Fake Error') |
| """ |
| else: |
| _FAKE_ERROR_EXTRA_MESSAGE = '' |
| |
| _PY_ERROR_LOG_MESSAGE = """\ |
| E1231 23:59:59.000000 12345 logging_functional_test_helper.py:65] This line is VLOG level -2 |
| E1231 23:59:59.000000 12345 logging_functional_test_helper.py:65] This line is log level -2 |
| E1231 23:59:59.000000 12345 logging_functional_test_helper.py:87] An Exception %s |
| Traceback (most recent call last): |
| File "logging_functional_test_helper.py", line 456, in _test_do_logging |
| raise OSError('Fake Error') |
| OSError: Fake Error |
| E0000 00:00:00.000000 12345 logging_functional_test_helper.py:123] Once more, just because |
| Traceback (most recent call last): |
| File "./logging_functional_test_helper.py", line 78, in _test_do_logging |
| raise OSError('Fake Error') |
| OSError: Fake Error |
| E0000 00:00:00.000000 12345 logging_functional_test_helper.py:123] Exception 2 %s |
| Traceback (most recent call last): |
| File "logging_functional_test_helper.py", line 456, in _test_do_logging |
| raise OSError('Fake Error') |
| OSError: Fake Error |
| E0000 00:00:00.000000 12345 logging_functional_test_helper.py:123] Non-exception |
| E0000 00:00:00.000000 12345 logging_functional_test_helper.py:123] Exception 3 |
| Traceback (most recent call last): |
| File "logging_functional_test_helper.py", line 456, in _test_do_logging |
| raise OSError('Fake Error') |
| OSError: Fake Error |
| E0000 00:00:00.000000 12345 logging_functional_test_helper.py:123] No traceback |
| {fake_error_extra}OSError: Fake Error |
| E1231 23:59:59.000000 12345 logging_functional_test_helper.py:90] Alarming Stuff |
| E0000 23:59:59.000000 12345 logging_functional_test_helper.py:92] Error first 1 of 2 |
| E0000 23:59:59.000000 12345 logging_functional_test_helper.py:93] Error 1 (every 3) |
| E0000 23:59:59.000000 12345 logging_functional_test_helper.py:92] Error first 2 of 2 |
| E0000 23:59:59.000000 12345 logging_functional_test_helper.py:93] Error 4 (every 3) |
| """.format(fake_error_extra=_FAKE_ERROR_EXTRA_MESSAGE) |
| |
| |
| _CRITICAL_DOWNGRADE_TO_ERROR_MESSAGE = """\ |
| E0000 00:00:00.000000 12345 logging_functional_test_helper.py:123] CRITICAL - A critical message |
| """ |
| |
| |
| _VERBOSITY_FLAG_TEST_PARAMETERS = ( |
| ('fatal', logging.FATAL), |
| ('error', logging.ERROR), |
| ('warning', logging.WARN), |
| ('info', logging.INFO), |
| ('debug', logging.DEBUG), |
| ('vlog1', 1), |
| ('vlog2', 2), |
| ('vlog3', 3)) |
| |
| |
| def _get_fatal_log_expectation(testcase, message, include_stacktrace): |
| """Returns the expectation for fatal logging tests. |
| |
| Args: |
| testcase: The TestCase instance. |
| message: The extra fatal logging message. |
| include_stacktrace: Whether or not to include stacktrace. |
| |
| Returns: |
| A callable, the expectation for fatal logging tests. It will be passed to |
| FunctionalTest._exec_test as third items in the expected_logs list. |
| See _exec_test's docstring for more information. |
| """ |
| def assert_logs(logs): |
| if os.name == 'nt': |
| # On Windows, it also dumps extra information at the end, something like: |
| # This application has requested the Runtime to terminate it in an |
| # unusual way. Please contact the application's support team for more |
| # information. |
| logs = '\n'.join(logs.split('\n')[:-3]) |
| format_string = ( |
| 'F1231 23:59:59.000000 12345 logging_functional_test_helper.py:175] ' |
| '%s message\n') |
| expected_logs = format_string % message |
| if include_stacktrace: |
| expected_logs += 'Stack trace:\n' |
| faulthandler_start = 'Fatal Python error: Aborted' |
| testcase.assertIn(faulthandler_start, logs) |
| log_message = logs.split(faulthandler_start)[0] |
| testcase.assertEqual(_munge_log(expected_logs), _munge_log(log_message)) |
| |
| return assert_logs |
| |
| |
| def _munge_log(buf): |
| """Remove timestamps, thread ids, filenames and line numbers from logs.""" |
| |
| # Remove all messages produced before the output to be tested. |
| buf = re.sub(r'(?:.|\n)*START OF TEST HELPER LOGS: IGNORE PREVIOUS.\n', |
| r'', |
| buf) |
| |
| # Greeting |
| buf = re.sub(r'(?m)^Log file created at: .*\n', |
| '', |
| buf) |
| buf = re.sub(r'(?m)^Running on machine: .*\n', |
| '', |
| buf) |
| buf = re.sub(r'(?m)^Binary: .*\n', |
| '', |
| buf) |
| buf = re.sub(r'(?m)^Log line format: .*\n', |
| '', |
| buf) |
| |
| # Verify thread id is logged as a non-negative quantity. |
| matched = re.match(r'(?m)^(\w)(\d\d\d\d \d\d:\d\d:\d\d\.\d\d\d\d\d\d) ' |
| r'([ ]*-?[0-9a-fA-f]+ )?([a-zA-Z<][\w._<>-]+):(\d+)', |
| buf) |
| if matched: |
| threadid = matched.group(3) |
| if int(threadid) < 0: |
| raise AssertionError("Negative threadid '%s' in '%s'" % (threadid, buf)) |
| |
| # Timestamp |
| buf = re.sub(r'(?m)' + logging.ABSL_LOGGING_PREFIX_REGEX, |
| r'\g<severity>0000 00:00:00.000000 12345 \g<filename>:123', |
| buf) |
| |
| # Traceback |
| buf = re.sub(r'(?m)^ File "(.*/)?([^"/]+)", line (\d+),', |
| r' File "\g<2>", line 456,', |
| buf) |
| |
| # Stack trace is too complicated for re, just assume it extends to end of |
| # output |
| buf = re.sub(r'(?sm)^Stack trace:\n.*', |
| r'Stack trace:\n', |
| buf) |
| buf = re.sub(r'(?sm)^\*\*\* Signal 6 received by PID.*\n.*', |
| r'Stack trace:\n', |
| buf) |
| buf = re.sub((r'(?sm)^\*\*\* ([A-Z]+) received by PID (\d+) ' |
| r'\(TID 0x([0-9a-f]+)\)' |
| r'( from PID \d+)?; stack trace: \*\*\*\n.*'), |
| r'Stack trace:\n', |
| buf) |
| buf = re.sub(r'(?sm)^\*\*\* Check failure stack trace: \*\*\*\n.*', |
| r'Stack trace:\n', |
| buf) |
| |
| if os.name == 'nt': |
| # On windows, we calls Python interpreter explicitly, so the file names |
| # include the full path. Strip them. |
| buf = re.sub(r'( File ").*(logging_functional_test_helper\.py", line )', |
| r'\1\2', |
| buf) |
| |
| return buf |
| |
| |
| def _verify_status(expected, actual, output): |
| if expected != actual: |
| raise AssertionError( |
| 'Test exited with unexpected status code %d (expected %d). ' |
| 'Output was:\n%s' % (actual, expected, output)) |
| |
| |
| def _verify_ok(status, output): |
| """Check that helper exited with no errors.""" |
| _verify_status(0, status, output) |
| |
| |
| def _verify_fatal(status, output): |
| """Check that helper died as expected.""" |
| # os.abort generates a SIGABRT signal (-6). On Windows, the process |
| # immediately returns an exit code of 3. |
| # See https://docs.python.org/3.6/library/os.html#os.abort. |
| expected_exit_code = 3 if os.name == 'nt' else -6 |
| _verify_status(expected_exit_code, status, output) |
| |
| |
| def _verify_assert(status, output): |
| """.Check that helper failed with assertion.""" |
| _verify_status(1, status, output) |
| |
| |
| class FunctionalTest(parameterized.TestCase): |
| """Functional tests using the logging_functional_test_helper script.""" |
| |
| def _get_helper(self): |
| helper_name = 'absl/logging/tests/logging_functional_test_helper' |
| return _bazelize_command.get_executable_path(helper_name) |
| |
| def _get_logs(self, |
| verbosity, |
| include_info_prefix=True): |
| logs = [] |
| if verbosity >= 3: |
| logs.append(_PY_VLOG3_LOG_MESSAGE) |
| if verbosity >= 2: |
| logs.append(_PY_VLOG2_LOG_MESSAGE) |
| if verbosity >= logging.DEBUG: |
| logs.append(_PY_DEBUG_LOG_MESSAGE) |
| |
| if verbosity >= logging.INFO: |
| if include_info_prefix: |
| logs.append(_PY_INFO_LOG_MESSAGE) |
| else: |
| logs.append(_PY_INFO_LOG_MESSAGE_NOPREFIX) |
| if verbosity >= logging.WARN: |
| logs.append(_PY_WARNING_LOG_MESSAGE) |
| if verbosity >= logging.ERROR: |
| logs.append(_PY_ERROR_LOG_MESSAGE) |
| |
| expected_logs = ''.join(logs) |
| expected_logs = expected_logs.replace( |
| "<type 'exceptions.OSError'>", "<class 'OSError'>") |
| return expected_logs |
| |
| def setUp(self): |
| super(FunctionalTest, self).setUp() |
| self._log_dir = tempfile.mkdtemp(dir=absltest.TEST_TMPDIR.value) |
| |
| def tearDown(self): |
| shutil.rmtree(self._log_dir) |
| super(FunctionalTest, self).tearDown() |
| |
| def _exec_test(self, |
| verify_exit_fn, |
| expected_logs, |
| test_name='do_logging', |
| pass_logtostderr=False, |
| use_absl_log_file=False, |
| show_info_prefix=1, |
| call_dict_config=False, |
| extra_args=()): |
| """Execute the helper script and verify its output. |
| |
| Args: |
| verify_exit_fn: A function taking (status, output). |
| expected_logs: List of tuples, or None if output shouldn't be checked. |
| Tuple is (log prefix, log type, expected contents): |
| - log prefix: A program name, or 'stderr'. |
| - log type: 'INFO', 'ERROR', etc. |
| - expected: Can be the following: |
| - A string |
| - A callable, called with the logs as a single argument |
| - None, means don't check contents of log file |
| test_name: Name to pass to helper. |
| pass_logtostderr: Pass --logtostderr to the helper script if True. |
| use_absl_log_file: If True, call |
| logging.get_absl_handler().use_absl_log_file() before test_fn in |
| logging_functional_test_helper. |
| show_info_prefix: --showprefixforinfo value passed to the helper script. |
| call_dict_config: True if helper script should call |
| logging.config.dictConfig. |
| extra_args: Iterable of str (optional, defaults to ()) - extra arguments |
| to pass to the helper script. |
| |
| Raises: |
| AssertionError: Assertion error when test fails. |
| """ |
| args = ['--log_dir=%s' % self._log_dir] |
| if pass_logtostderr: |
| args.append('--logtostderr') |
| if not show_info_prefix: |
| args.append('--noshowprefixforinfo') |
| args += extra_args |
| |
| # Execute helper in subprocess. |
| env = os.environ.copy() |
| env.update({ |
| 'TEST_NAME': test_name, |
| 'USE_ABSL_LOG_FILE': '%d' % (use_absl_log_file,), |
| 'CALL_DICT_CONFIG': '%d' % (call_dict_config,), |
| }) |
| cmd = [self._get_helper()] + args |
| |
| print('env: %s' % env, file=sys.stderr) |
| print('cmd: %s' % cmd, file=sys.stderr) |
| process = subprocess.Popen( |
| cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, env=env, |
| universal_newlines=True) |
| output, _ = process.communicate() |
| status = process.returncode |
| |
| # Verify exit status. |
| verify_exit_fn(status, output) |
| |
| # Check outputs? |
| if expected_logs is None: |
| return |
| |
| # Get list of log files. |
| logs = os.listdir(self._log_dir) |
| logs = fnmatch.filter(logs, '*.log.*') |
| logs.append('stderr') |
| |
| # Look for a log matching each expected pattern. |
| matched = [] |
| unmatched = [] |
| unexpected = logs[:] |
| for log_prefix, log_type, expected in expected_logs: |
| # What pattern? |
| if log_prefix == 'stderr': |
| assert log_type is None |
| pattern = 'stderr' |
| else: |
| pattern = r'%s[.].*[.]log[.]%s[.][\d.-]*$' % (log_prefix, log_type) |
| |
| # Is it there |
| for basename in logs: |
| if re.match(pattern, basename): |
| matched.append([expected, basename]) |
| unexpected.remove(basename) |
| break |
| else: |
| unmatched.append(pattern) |
| |
| # Mismatch? |
| errors = '' |
| if unmatched: |
| errors += 'The following log files were expected but not found: %s' % ( |
| '\n '.join(unmatched)) |
| if unexpected: |
| if errors: |
| errors += '\n' |
| errors += 'The following log files were not expected: %s' % ( |
| '\n '.join(unexpected)) |
| if errors: |
| raise AssertionError(errors) |
| |
| # Compare contents of matches. |
| for (expected, basename) in matched: |
| if expected is None: |
| continue |
| |
| if basename == 'stderr': |
| actual = output |
| else: |
| path = os.path.join(self._log_dir, basename) |
| with open(path, encoding='utf-8') as f: |
| actual = f.read() |
| |
| if callable(expected): |
| try: |
| expected(actual) |
| except AssertionError: |
| print('expected_logs assertion failed, actual {} log:\n{}'.format( |
| basename, actual), file=sys.stderr) |
| raise |
| elif isinstance(expected, str): |
| self.assertMultiLineEqual(_munge_log(expected), _munge_log(actual), |
| '%s differs' % basename) |
| else: |
| self.fail( |
| 'Invalid value found for expected logs: {}, type: {}'.format( |
| expected, type(expected))) |
| |
| @parameterized.named_parameters( |
| ('', False), |
| ('logtostderr', True)) |
| def test_py_logging(self, logtostderr): |
| # Python logging by default logs to stderr. |
| self._exec_test( |
| _verify_ok, |
| [['stderr', None, self._get_logs(logging.INFO)]], |
| pass_logtostderr=logtostderr) |
| |
| def test_py_logging_use_absl_log_file(self): |
| # Python logging calling use_absl_log_file causes also log to files. |
| self._exec_test( |
| _verify_ok, |
| [['stderr', None, ''], |
| ['absl_log_file', 'INFO', self._get_logs(logging.INFO)]], |
| use_absl_log_file=True) |
| |
| def test_py_logging_use_absl_log_file_logtostderr(self): |
| # Python logging asked to log to stderr even though use_absl_log_file |
| # is called. |
| self._exec_test( |
| _verify_ok, |
| [['stderr', None, self._get_logs(logging.INFO)]], |
| pass_logtostderr=True, |
| use_absl_log_file=True) |
| |
| @parameterized.named_parameters( |
| ('', False), |
| ('logtostderr', True)) |
| def test_py_logging_noshowprefixforinfo(self, logtostderr): |
| self._exec_test( |
| _verify_ok, |
| [['stderr', None, self._get_logs(logging.INFO, |
| include_info_prefix=False)]], |
| pass_logtostderr=logtostderr, |
| show_info_prefix=0) |
| |
| def test_py_logging_noshowprefixforinfo_use_absl_log_file(self): |
| self._exec_test( |
| _verify_ok, |
| [['stderr', None, ''], |
| ['absl_log_file', 'INFO', self._get_logs(logging.INFO)]], |
| show_info_prefix=0, |
| use_absl_log_file=True) |
| |
| def test_py_logging_noshowprefixforinfo_use_absl_log_file_logtostderr(self): |
| self._exec_test( |
| _verify_ok, |
| [['stderr', None, self._get_logs(logging.INFO, |
| include_info_prefix=False)]], |
| pass_logtostderr=True, |
| show_info_prefix=0, |
| use_absl_log_file=True) |
| |
| def test_py_logging_noshowprefixforinfo_verbosity(self): |
| self._exec_test( |
| _verify_ok, |
| [['stderr', None, self._get_logs(logging.DEBUG)]], |
| pass_logtostderr=True, |
| show_info_prefix=0, |
| use_absl_log_file=True, |
| extra_args=['-v=1']) |
| |
| def test_py_logging_fatal_main_thread_only(self): |
| self._exec_test( |
| _verify_fatal, |
| [['stderr', None, _get_fatal_log_expectation( |
| self, 'fatal_main_thread_only', False)]], |
| test_name='fatal_main_thread_only') |
| |
| def test_py_logging_fatal_with_other_threads(self): |
| self._exec_test( |
| _verify_fatal, |
| [['stderr', None, _get_fatal_log_expectation( |
| self, 'fatal_with_other_threads', False)]], |
| test_name='fatal_with_other_threads') |
| |
| def test_py_logging_fatal_non_main_thread(self): |
| self._exec_test( |
| _verify_fatal, |
| [['stderr', None, _get_fatal_log_expectation( |
| self, 'fatal_non_main_thread', False)]], |
| test_name='fatal_non_main_thread') |
| |
| def test_py_logging_critical_non_absl(self): |
| self._exec_test( |
| _verify_ok, |
| [['stderr', None, _CRITICAL_DOWNGRADE_TO_ERROR_MESSAGE]], |
| test_name='critical_from_non_absl_logger') |
| |
| def test_py_logging_skip_log_prefix(self): |
| self._exec_test( |
| _verify_ok, |
| [['stderr', None, '']], |
| test_name='register_frame_to_skip') |
| |
| def test_py_logging_flush(self): |
| self._exec_test( |
| _verify_ok, |
| [['stderr', None, '']], |
| test_name='flush') |
| |
| @parameterized.named_parameters(*_VERBOSITY_FLAG_TEST_PARAMETERS) |
| def test_py_logging_verbosity_stderr(self, verbosity): |
| """Tests -v/--verbosity flag with python logging to stderr.""" |
| v_flag = '-v=%d' % verbosity |
| self._exec_test( |
| _verify_ok, |
| [['stderr', None, self._get_logs(verbosity)]], |
| extra_args=[v_flag]) |
| |
| @parameterized.named_parameters(*_VERBOSITY_FLAG_TEST_PARAMETERS) |
| def test_py_logging_verbosity_file(self, verbosity): |
| """Tests -v/--verbosity flag with Python logging to stderr.""" |
| v_flag = '-v=%d' % verbosity |
| self._exec_test( |
| _verify_ok, |
| [['stderr', None, ''], |
| # When using python logging, it only creates a file named INFO, |
| # unlike C++ it also creates WARNING and ERROR files. |
| ['absl_log_file', 'INFO', self._get_logs(verbosity)]], |
| use_absl_log_file=True, |
| extra_args=[v_flag]) |
| |
| def test_stderrthreshold_py_logging(self): |
| """Tests --stderrthreshold.""" |
| |
| stderr_logs = '''\ |
| I0000 00:00:00.000000 12345 logging_functional_test_helper.py:123] FLAGS.stderrthreshold=debug, debug log |
| I0000 00:00:00.000000 12345 logging_functional_test_helper.py:123] FLAGS.stderrthreshold=debug, info log |
| W0000 00:00:00.000000 12345 logging_functional_test_helper.py:123] FLAGS.stderrthreshold=debug, warning log |
| E0000 00:00:00.000000 12345 logging_functional_test_helper.py:123] FLAGS.stderrthreshold=debug, error log |
| I0000 00:00:00.000000 12345 logging_functional_test_helper.py:123] FLAGS.stderrthreshold=info, info log |
| W0000 00:00:00.000000 12345 logging_functional_test_helper.py:123] FLAGS.stderrthreshold=info, warning log |
| E0000 00:00:00.000000 12345 logging_functional_test_helper.py:123] FLAGS.stderrthreshold=info, error log |
| W0000 00:00:00.000000 12345 logging_functional_test_helper.py:123] FLAGS.stderrthreshold=warning, warning log |
| E0000 00:00:00.000000 12345 logging_functional_test_helper.py:123] FLAGS.stderrthreshold=warning, error log |
| E0000 00:00:00.000000 12345 logging_functional_test_helper.py:123] FLAGS.stderrthreshold=error, error log |
| ''' |
| |
| expected_logs = [ |
| ['stderr', None, stderr_logs], |
| ['absl_log_file', 'INFO', None], |
| ] |
| # Set verbosity to debug to test stderrthreshold == debug. |
| extra_args = ['-v=1'] |
| |
| self._exec_test( |
| _verify_ok, |
| expected_logs, |
| test_name='stderrthreshold', |
| extra_args=extra_args, |
| use_absl_log_file=True) |
| |
| def test_std_logging_py_logging(self): |
| """Tests logs from std logging.""" |
| stderr_logs = '''\ |
| I0000 00:00:00.000000 12345 logging_functional_test_helper.py:123] std debug log |
| I0000 00:00:00.000000 12345 logging_functional_test_helper.py:123] std info log |
| W0000 00:00:00.000000 12345 logging_functional_test_helper.py:123] std warning log |
| E0000 00:00:00.000000 12345 logging_functional_test_helper.py:123] std error log |
| ''' |
| expected_logs = [['stderr', None, stderr_logs]] |
| |
| extra_args = ['-v=1', '--logtostderr'] |
| self._exec_test( |
| _verify_ok, |
| expected_logs, |
| test_name='std_logging', |
| extra_args=extra_args) |
| |
| def test_bad_exc_info_py_logging(self): |
| |
| def assert_stderr(stderr): |
| # The exact message differs among different Python versions. So it just |
| # asserts some certain information is there. |
| self.assertIn('Traceback (most recent call last):', stderr) |
| self.assertIn('IndexError', stderr) |
| |
| expected_logs = [ |
| ['stderr', None, assert_stderr], |
| ['absl_log_file', 'INFO', '']] |
| |
| self._exec_test( |
| _verify_ok, |
| expected_logs, |
| test_name='bad_exc_info', |
| use_absl_log_file=True) |
| |
| def test_verbosity_logger_levels_flag_ordering(self): |
| """Make sure last-specified flag wins.""" |
| |
| def assert_error_level_logged(stderr): |
| lines = stderr.splitlines() |
| for line in lines: |
| self.assertIn('std error log', line) |
| |
| self._exec_test( |
| _verify_ok, |
| test_name='std_logging', |
| expected_logs=[('stderr', None, assert_error_level_logged)], |
| extra_args=['-v=1', '--logger_levels=:ERROR']) |
| |
| def assert_debug_level_logged(stderr): |
| lines = stderr.splitlines() |
| for line in lines: |
| self.assertRegex(line, 'std (debug|info|warning|error) log') |
| |
| self._exec_test( |
| _verify_ok, |
| test_name='std_logging', |
| expected_logs=[('stderr', None, assert_debug_level_logged)], |
| extra_args=['--logger_levels=:ERROR', '-v=1']) |
| |
| def test_none_exc_info_py_logging(self): |
| |
| expected_stderr = '' |
| expected_info = '''\ |
| I0000 00:00:00.000000 12345 logging_functional_test_helper.py:123] None exc_info |
| ''' |
| expected_info += 'NoneType: None\n' |
| |
| expected_logs = [ |
| ['stderr', None, expected_stderr], |
| ['absl_log_file', 'INFO', expected_info]] |
| |
| self._exec_test( |
| _verify_ok, |
| expected_logs, |
| test_name='none_exc_info', |
| use_absl_log_file=True) |
| |
| def test_unicode_py_logging(self): |
| |
| def get_stderr_message(stderr, name): |
| match = re.search( |
| '-- begin {} --\n(.*)-- end {} --'.format(name, name), |
| stderr, re.MULTILINE | re.DOTALL) |
| self.assertTrue( |
| match, 'Cannot find stderr message for test {}'.format(name)) |
| return match.group(1) |
| |
| def assert_stderr(stderr): |
| """Verifies that it writes correct information to stderr for Python 3. |
| |
| There are no unicode errors in Python 3. |
| |
| Args: |
| stderr: the message from stderr. |
| """ |
| # Successful logs: |
| for name in ( |
| 'unicode', 'unicode % unicode', 'bytes % bytes', 'unicode % bytes', |
| 'bytes % unicode', 'unicode % iso8859-15', 'str % exception', |
| 'str % exception'): |
| logging.info('name = %s', name) |
| self.assertEqual('', get_stderr_message(stderr, name)) |
| |
| expected_logs = [['stderr', None, assert_stderr]] |
| |
| info_log = u'''\ |
| I0000 00:00:00.000000 12345 logging_functional_test_helper.py:123] G\u00eete: Ch\u00e2tonnaye |
| I0000 00:00:00.000000 12345 logging_functional_test_helper.py:123] G\u00eete: Ch\u00e2tonnaye |
| I0000 00:00:00.000000 12345 logging_functional_test_helper.py:123] b'G\\xc3\\xaete: b'Ch\\xc3\\xa2tonnaye'' |
| I0000 00:00:00.000000 12345 logging_functional_test_helper.py:123] G\u00eete: b'Ch\\xc3\\xa2tonnaye' |
| I0000 00:00:00.000000 12345 logging_functional_test_helper.py:123] b'G\\xc3\\xaete: Ch\u00e2tonnaye' |
| I0000 00:00:00.000000 12345 logging_functional_test_helper.py:123] G\u00eete: b'Ch\\xe2tonnaye' |
| I0000 00:00:00.000000 12345 logging_functional_test_helper.py:123] exception: Ch\u00e2tonnaye |
| ''' |
| expected_logs.append(['absl_log_file', 'INFO', info_log]) |
| |
| self._exec_test( |
| _verify_ok, |
| expected_logs, |
| test_name='unicode', |
| use_absl_log_file=True) |
| |
| |
| if __name__ == '__main__': |
| absltest.main() |