Allow tty output to be timestamped (#10554)
* Spack debug output now includes microsecond-granularity timestamps. * Timestamps can also be enabled with the `--timestamp` command line argument.
This commit is contained in:
		| @@ -3,6 +3,7 @@ | |||||||
| # | # | ||||||
| # SPDX-License-Identifier: (Apache-2.0 OR MIT) | # SPDX-License-Identifier: (Apache-2.0 OR MIT) | ||||||
|  |  | ||||||
|  | from datetime import datetime | ||||||
| import fcntl | import fcntl | ||||||
| import os | import os | ||||||
| import struct | import struct | ||||||
| @@ -18,6 +19,10 @@ | |||||||
| _debug = False | _debug = False | ||||||
| _verbose = False | _verbose = False | ||||||
| _stacktrace = False | _stacktrace = False | ||||||
|  | _timestamp = False | ||||||
|  | _msg_enabled = True | ||||||
|  | _warn_enabled = True | ||||||
|  | _error_enabled = True | ||||||
| indent = "  " | indent = "  " | ||||||
|  |  | ||||||
|  |  | ||||||
| @@ -43,6 +48,65 @@ def set_verbose(flag): | |||||||
|     _verbose = flag |     _verbose = flag | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def set_timestamp(flag): | ||||||
|  |     global _timestamp | ||||||
|  |     _timestamp = flag | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def set_msg_enabled(flag): | ||||||
|  |     global _msg_enabled | ||||||
|  |     _msg_enabled = flag | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def set_warn_enabled(flag): | ||||||
|  |     global _warn_enabled | ||||||
|  |     _warn_enabled = flag | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def set_error_enabled(flag): | ||||||
|  |     global _error_enabled | ||||||
|  |     _error_enabled = flag | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def msg_enabled(): | ||||||
|  |     return _msg_enabled | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def warn_enabled(): | ||||||
|  |     return _warn_enabled | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def error_enabled(): | ||||||
|  |     return _error_enabled | ||||||
|  |  | ||||||
|  |  | ||||||
|  | class SuppressOutput: | ||||||
|  |     """Class for disabling output in a scope using 'with' keyword""" | ||||||
|  |  | ||||||
|  |     def __init__(self, | ||||||
|  |                  msg_enabled=True, | ||||||
|  |                  warn_enabled=True, | ||||||
|  |                  error_enabled=True): | ||||||
|  |  | ||||||
|  |         self._msg_enabled_initial = _msg_enabled | ||||||
|  |         self._warn_enabled_initial = _warn_enabled | ||||||
|  |         self._error_enabled_initial = _error_enabled | ||||||
|  |  | ||||||
|  |         self._msg_enabled = msg_enabled | ||||||
|  |         self._warn_enabled = warn_enabled | ||||||
|  |         self._error_enabled = error_enabled | ||||||
|  |  | ||||||
|  |     def __enter__(self): | ||||||
|  |         set_msg_enabled(self._msg_enabled) | ||||||
|  |         set_warn_enabled(self._warn_enabled) | ||||||
|  |         set_error_enabled(self._error_enabled) | ||||||
|  |  | ||||||
|  |     def __exit__(self, exc_type, exc_val, exc_tb): | ||||||
|  |         set_msg_enabled(self._msg_enabled_initial) | ||||||
|  |         set_warn_enabled(self._warn_enabled_initial) | ||||||
|  |         set_error_enabled(self._error_enabled_initial) | ||||||
|  |  | ||||||
|  |  | ||||||
| def set_stacktrace(flag): | def set_stacktrace(flag): | ||||||
|     global _stacktrace |     global _stacktrace | ||||||
|     _stacktrace = flag |     _stacktrace = flag | ||||||
| @@ -65,15 +129,28 @@ def process_stacktrace(countback): | |||||||
|     return st_text |     return st_text | ||||||
|  |  | ||||||
|  |  | ||||||
|  | def get_timestamp(force=False): | ||||||
|  |     """Get a string timestamp""" | ||||||
|  |     if _debug or _timestamp or force: | ||||||
|  |         return datetime.now().strftime("[%Y-%m-%d-%H:%M:%S.%f] ") | ||||||
|  |     else: | ||||||
|  |         return '' | ||||||
|  |  | ||||||
|  |  | ||||||
| def msg(message, *args, **kwargs): | def msg(message, *args, **kwargs): | ||||||
|  |     if not msg_enabled(): | ||||||
|  |         return | ||||||
|  |  | ||||||
|     newline = kwargs.get('newline', True) |     newline = kwargs.get('newline', True) | ||||||
|     st_text = "" |     st_text = "" | ||||||
|     if _stacktrace: |     if _stacktrace: | ||||||
|         st_text = process_stacktrace(2) |         st_text = process_stacktrace(2) | ||||||
|     if newline: |     if newline: | ||||||
|         cprint("@*b{%s==>} %s" % (st_text, cescape(message))) |         cprint("@*b{%s==>} %s%s" % ( | ||||||
|  |             st_text, get_timestamp(), cescape(message))) | ||||||
|     else: |     else: | ||||||
|         cwrite("@*b{%s==>} %s" % (st_text, cescape(message))) |         cwrite("@*b{%s==>} %s%s" % ( | ||||||
|  |             st_text, get_timestamp(), cescape(message))) | ||||||
|     for arg in args: |     for arg in args: | ||||||
|         print(indent + str(arg)) |         print(indent + str(arg)) | ||||||
|  |  | ||||||
| @@ -88,7 +165,8 @@ def info(message, *args, **kwargs): | |||||||
|     st_text = "" |     st_text = "" | ||||||
|     if _stacktrace: |     if _stacktrace: | ||||||
|         st_text = process_stacktrace(st_countback) |         st_text = process_stacktrace(st_countback) | ||||||
|     cprint("@%s{%s==>} %s" % (format, st_text, cescape(str(message))), |     cprint("@%s{%s==>} %s%s" % ( | ||||||
|  |         format, st_text, get_timestamp(), cescape(str(message))), | ||||||
|         stream=stream) |         stream=stream) | ||||||
|     for arg in args: |     for arg in args: | ||||||
|         if wrap: |         if wrap: | ||||||
| @@ -115,12 +193,18 @@ def debug(message, *args, **kwargs): | |||||||
|  |  | ||||||
|  |  | ||||||
| def error(message, *args, **kwargs): | def error(message, *args, **kwargs): | ||||||
|  |     if not error_enabled(): | ||||||
|  |         return | ||||||
|  |  | ||||||
|     kwargs.setdefault('format', '*r') |     kwargs.setdefault('format', '*r') | ||||||
|     kwargs.setdefault('stream', sys.stderr) |     kwargs.setdefault('stream', sys.stderr) | ||||||
|     info("Error: " + str(message), *args, **kwargs) |     info("Error: " + str(message), *args, **kwargs) | ||||||
|  |  | ||||||
|  |  | ||||||
| def warn(message, *args, **kwargs): | def warn(message, *args, **kwargs): | ||||||
|  |     if not warn_enabled(): | ||||||
|  |         return | ||||||
|  |  | ||||||
|     kwargs.setdefault('format', '*Y') |     kwargs.setdefault('format', '*Y') | ||||||
|     kwargs.setdefault('stream', sys.stderr) |     kwargs.setdefault('stream', sys.stderr) | ||||||
|     info("Warning: " + str(message), *args, **kwargs) |     info("Warning: " + str(message), *args, **kwargs) | ||||||
|   | |||||||
| @@ -238,7 +238,7 @@ def fetch(self): | |||||||
|         if not spack.config.get('config:verify_ssl'): |         if not spack.config.get('config:verify_ssl'): | ||||||
|             curl_args.append('-k') |             curl_args.append('-k') | ||||||
|  |  | ||||||
|         if sys.stdout.isatty(): |         if sys.stdout.isatty() and tty.msg_enabled(): | ||||||
|             curl_args.append('-#')  # status bar when using a tty |             curl_args.append('-#')  # status bar when using a tty | ||||||
|         else: |         else: | ||||||
|             curl_args.append('-sS')  # just errors when not. |             curl_args.append('-sS')  # just errors when not. | ||||||
|   | |||||||
| @@ -329,6 +329,9 @@ def make_argument_parser(**kwargs): | |||||||
|     parser.add_argument( |     parser.add_argument( | ||||||
|         '-d', '--debug', action='store_true', |         '-d', '--debug', action='store_true', | ||||||
|         help="write out debug logs during compile") |         help="write out debug logs during compile") | ||||||
|  |     parser.add_argument( | ||||||
|  |         '--timestamp', action='store_true', | ||||||
|  |         help="Add a timestamp to tty output") | ||||||
|     parser.add_argument( |     parser.add_argument( | ||||||
|         '--pdb', action='store_true', |         '--pdb', action='store_true', | ||||||
|         help="run spack under the pdb debugger") |         help="run spack under the pdb debugger") | ||||||
| @@ -399,6 +402,9 @@ def setup_main_options(args): | |||||||
|         spack.util.debug.register_interrupt_handler() |         spack.util.debug.register_interrupt_handler() | ||||||
|         spack.config.set('config:debug', True, scope='command_line') |         spack.config.set('config:debug', True, scope='command_line') | ||||||
|  |  | ||||||
|  |     if args.timestamp: | ||||||
|  |         tty.set_timestamp(True) | ||||||
|  |  | ||||||
|     # override lock configuration if passed on command line |     # override lock configuration if passed on command line | ||||||
|     if args.locks is not None: |     if args.locks is not None: | ||||||
|         spack.util.lock.check_lock_safety(spack.paths.prefix) |         spack.util.lock.check_lock_safety(spack.paths.prefix) | ||||||
|   | |||||||
| @@ -1476,6 +1476,9 @@ def build_process(): | |||||||
|                         # Save the build environment in a file before building. |                         # Save the build environment in a file before building. | ||||||
|                         dump_environment(self.env_path) |                         dump_environment(self.env_path) | ||||||
|  |  | ||||||
|  |                         # cache debug settings | ||||||
|  |                         debug_enabled = tty.is_debug() | ||||||
|  |  | ||||||
|                         # Spawn a daemon that reads from a pipe and redirects |                         # Spawn a daemon that reads from a pipe and redirects | ||||||
|                         # everything to log_path |                         # everything to log_path | ||||||
|                         with log_output(self.log_path, echo, True) as logger: |                         with log_output(self.log_path, echo, True) as logger: | ||||||
| @@ -1483,8 +1486,11 @@ def build_process(): | |||||||
|                                     self.phases, self._InstallPhase_phases): |                                     self.phases, self._InstallPhase_phases): | ||||||
|  |  | ||||||
|                                 with logger.force_echo(): |                                 with logger.force_echo(): | ||||||
|  |                                     inner_debug = tty.is_debug() | ||||||
|  |                                     tty.set_debug(debug_enabled) | ||||||
|                                     tty.msg( |                                     tty.msg( | ||||||
|                                         "Executing phase: '%s'" % phase_name) |                                         "Executing phase: '%s'" % phase_name) | ||||||
|  |                                     tty.set_debug(inner_debug) | ||||||
|  |  | ||||||
|                                 # Redirect stdout and stderr to daemon pipe |                                 # Redirect stdout and stderr to daemon pipe | ||||||
|                                 phase = getattr(self, phase_attr) |                                 phase = getattr(self, phase_attr) | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ | |||||||
| import argparse | import argparse | ||||||
| import os | import os | ||||||
| import filecmp | import filecmp | ||||||
|  | import re | ||||||
| from six.moves import builtins | from six.moves import builtins | ||||||
|  |  | ||||||
| import pytest | import pytest | ||||||
| @@ -129,7 +130,7 @@ def test_package_output(tmpdir, capsys, install_mockery, mock_fetch): | |||||||
|  |  | ||||||
|     # make sure that output from the actual package file appears in the |     # make sure that output from the actual package file appears in the | ||||||
|     # right place in the build log. |     # right place in the build log. | ||||||
|     assert "BEFORE INSTALL\n==> './configure'" in out |     assert re.search(r"BEFORE INSTALL\n==>( \[.+\])? './configure'", out) | ||||||
|     assert "'install'\nAFTER INSTALL" in out |     assert "'install'\nAFTER INSTALL" in out | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 sknigh
					sknigh