From 21c2410a6da4b4b8941e7b7b11ff48d10cbcbb5c Mon Sep 17 00:00:00 2001 From: Genevieve Helsel Date: Fri, 10 Sep 2021 23:53:45 -0700 Subject: [PATCH] pretty print rage sections and normalize underlining Summary: the rage summary is getting hard to quickly parse, so this underlines each section header, as well as unifies underline looks (with `eden stats`). This adopts the underline code from `eden du` and makes it a util function for shareability. Differential Revision: D30857773 fbshipit-source-id: 66b5b06f5b0125304d45d3465a8bc2248693b791 --- eden/fs/cli/main.py | 14 +++++------- eden/fs/cli/rage.py | 42 ++++++++++++++++++++--------------- eden/fs/cli/stats.py | 23 ++++--------------- eden/fs/cli/util.py | 5 +++++ eden/integration/rage_test.py | 6 +++-- 5 files changed, 42 insertions(+), 48 deletions(-) diff --git a/eden/fs/cli/main.py b/eden/fs/cli/main.py index bb1c6a05e9..528ee76202 100644 --- a/eden/fs/cli/main.py +++ b/eden/fs/cli/main.py @@ -214,7 +214,7 @@ files outside the repo rather than to ignore and clean them up.\n""", backing_repos = set() all_redirections = set() - self.underlined("Mounts") + self.write_ui(util.underlined("Mounts")) # print all mounts together. for mount in mounts: @@ -229,7 +229,7 @@ files outside the repo rather than to ignore and clean them up.\n""", self.usage_for_mount(mount, args, clean, deep_clean) - self.underlined("Redirections") + self.write_ui(util.underlined("Redirections")) for mount in mounts: instance, checkout, _rel_path = require_checkout(args, mount) @@ -249,7 +249,7 @@ parent of the buck-out directory. ) if backing_repos: - self.underlined("Backing repos") + self.write_ui(util.underlined("Backing repos")) for backing in backing_repos: self.writeln_ui(backing) @@ -310,7 +310,7 @@ space by running: return 0 def make_summary(self, clean, deep_clean): - self.underlined("Summary") + self.write_ui(util.underlined("Summary")) type_labels = { "materialized": "Materialized files", "redirection": "Redirections", @@ -405,10 +405,6 @@ space by running: def writeln_ui(self, message, fg=None) -> None: self.write_ui(f"{message}\n") - def underlined(self, message) -> None: - underline = "-" * len(message) - self.write_ui(f"\n{message}\n{underline}\n") - def usage_for_dir( self, path, usage_type: str, print_label: Optional[str] = None ) -> None: @@ -442,7 +438,7 @@ space by running: def shared_usage(self, instance: EdenInstance, clean: bool) -> None: logs_dir = instance.state_dir / "logs" storage_dir = instance.state_dir / "storage" - self.underlined("Shared space") + self.write_ui(util.underlined("Shared space")) self.usage_for_dir(logs_dir, "shared") self.usage_for_dir(storage_dir, "shared") if clean: diff --git a/eden/fs/cli/rage.py b/eden/fs/cli/rage.py index 1bda06aa14..eb9354c6a7 100644 --- a/eden/fs/cli/rage.py +++ b/eden/fs/cli/rage.py @@ -25,15 +25,21 @@ from . import ( redirect as redirect_mod, stats as stats_mod, ui as ui_mod, + util as util_mod, version as version_mod, top as top_mod, ) from .config import EdenInstance +def section_title(message: str, out: IO[bytes]) -> None: + out.write(util_mod.underlined(message).encode()) + + def print_diagnostic_info( instance: EdenInstance, out: IO[bytes], dry_run: bool ) -> None: + section_title("System info:", out) header = ( f"User : {getpass.getuser()}\n" f"Hostname : {socket.gethostname()}\n" @@ -49,7 +55,7 @@ def print_diagnostic_info( health_status = instance.check_health() if health_status.is_healthy(): - out.write(b"\n") + section_title("Build info:", out) debug_mod.do_buildinfo(instance, out) out.write(b"uptime: ") instance.do_uptime(pretty=False, out=out) @@ -69,7 +75,7 @@ def print_diagnostic_info( print_eden_redirections(instance, out) - out.write(b"\nList of mount points:\n") + section_title("List of mount points:", out) mountpoint_paths = [] for key in sorted(instance.get_mount_paths()): out.write(key.encode() + b"\n") @@ -139,10 +145,9 @@ def print_eden_doctor_report(instance: EdenInstance, out: IO[bytes]) -> None: doctor_rc = doctor_mod.cure_what_ails_you( instance, dry_run=True, out=ui_mod.PlainOutput(doctor_output) ) - out.write( - b"\neden doctor --dry-run (exit code %d):\n%s\n" - % (doctor_rc, doctor_output.getvalue().encode()) - ) + doctor_report_title = f"eden doctor --dry-run (exit code {doctor_rc}):" + section_title(doctor_report_title, out) + out.write(doctor_output.getvalue().encode()) except Exception: out.write(b"\nUnexpected exception thrown while running eden doctor checks:\n") out.write(traceback.format_exc().encode("utf-8") + b"\n") @@ -198,18 +203,19 @@ def print_expanded_log_file(path: Path, processor: str, out: IO[bytes]) -> None: pattern = re.compile("^.*\\n[a-zA-Z0-9_.-]*: .*\\n$") match = pattern.match(stdout) + section_title("Verbose EdenFS logs:", out) if not match: - out.write(b"Verbose EdenFS logs: %s\n" % stdout.encode()) + out.write(stdout.encode()) else: paste, _ = stdout.split("\n")[1].split(": ") - out.write(b"Verbose EdenFS logs: %s\n" % paste.encode()) + out.write(paste.encode()) except Exception as e: out.write(b"Error generating expanded EdenFS logs: %s\n" % str(e).encode()) def print_tail_of_log_file(path: Path, out: IO[bytes]) -> None: try: - out.write(b"\nMost recent EdenFS logs:\n") + section_title("Most recent EdenFS logs:", out) LOG_AMOUNT = 20 * 1024 with path.open("rb") as logfile: size = logfile.seek(0, io.SEEK_END) @@ -247,7 +253,7 @@ def _get_running_eden_process_windows() -> List[Tuple[str, str, str, str, str, s def print_running_eden_process(out: IO[bytes]) -> None: try: - out.write(b"\nList of running EdenFS processes:\n") + section_title("List of running EdenFS processes:", out) if sys.platform == "win32": lines = _get_running_eden_process_windows() else: @@ -279,7 +285,7 @@ def print_edenfs_process_tree(pid: int, out: IO[bytes]) -> None: if sys.platform != "linux": return try: - out.write(b"\nedenfs process tree:\n") + section_title("EdenFS process tree:", out) output = subprocess.check_output(["ps", "-o", "sid=", "-p", str(pid)]) sid = output.decode("utf-8").strip() output = subprocess.check_output( @@ -295,7 +301,7 @@ def print_eden_redirections(instance: EdenInstance, out: IO[bytes]) -> None: # TODO(zeyi): fix this once eden redirect is working on Windows return try: - out.write(b"\nedenfs redirections:\n") + section_title("EdenFS redirections:", out) checkouts = instance.get_checkouts() for checkout in checkouts: out.write(bytes(checkout.path) + b"\n") @@ -304,24 +310,24 @@ def print_eden_redirections(instance: EdenInstance, out: IO[bytes]) -> None: output = output.replace("\n", "\n\t") out.write(b"\t" + output.encode() + b"\n") except Exception as e: - out.write(b"Error getting edenfs redirections %s\n" % str(e).encode()) + out.write(b"Error getting EdenFS redirections %s\n" % str(e).encode()) out.write(traceback.format_exc().encode() + b"\n") def print_thrift_counters(instance: EdenInstance, out: IO[bytes]) -> None: try: - out.write(b"\nedenfs counters:\n") + out.write(util_mod.underlined("EdenFS counters:").encode()) with instance.get_thrift_client_legacy() as client: counters = client.getRegexCounters(top_mod.COUNTER_REGEX) for key, value in counters.items(): out.write(f"{key}: {value}\n".encode()) except Exception as e: - out.write(b"Error getting edenfs Thrift counters: %s\n" % str(e).encode()) + out.write(b"Error getting EdenFS Thrift counters: %s\n" % str(e).encode()) def print_env_variables(out: IO[bytes]) -> None: try: - out.write(b"\nEnvironment Variables:\n") + section_title("Environment variables:", out) for k, v in os.environ.items(): out.write(f"{k}={v}\n".encode()) except Exception as e: @@ -330,7 +336,7 @@ def print_env_variables(out: IO[bytes]) -> None: def print_eden_config(instance: EdenInstance, out: IO[bytes]) -> None: try: - out.write(b"\nedenfs config:\n") + section_title("EdenFS config:", out) instance.print_full_config(out) except Exception as e: - out.write(f"Error printing edenfs config: {e}\n".encode()) + out.write(f"Error printing EdenFS config: {e}\n".encode()) diff --git a/eden/fs/cli/stats.py b/eden/fs/cli/stats.py index 503807a5a8..a75610810f 100644 --- a/eden/fs/cli/stats.py +++ b/eden/fs/cli/stats.py @@ -21,7 +21,7 @@ from facebook.eden.ttypes import GetStatInfoParams from thrift.protocol.TSimpleJSONProtocol import TSimpleJSONProtocolFactory from thrift.util import Serializer as ThriftSerializer -from . import cmd_util, stats_print, subcmd as subcmd_mod +from . import cmd_util, stats_print, subcmd as subcmd_mod, util as util_mod from .config import EdenInstance from .subcmd import Subcmd @@ -82,28 +82,13 @@ def print_stats(stat_info, out: io.TextIOWrapper): blob_cache_size = None blob_cache_entry_count = None - out.write( - textwrap.dedent( - f"""\ - edenfs memory usage - ▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔▔ - private bytes: {private_bytes} ({resident_bytes} resident) - """ - ) - ) + out.write(util_mod.underlined("EdenFS memory usage:")) + out.write(f"private bytes: {private_bytes} ({resident_bytes} resident)\n") if blob_cache_size is not None and blob_cache_entry_count is not None: out.write(f"blob cache: {blob_cache_size} in {blob_cache_entry_count} blobs\n") - out.write( - textwrap.dedent( - f"""\ - - active mounts - ▔▔▔▔▔▔▔▔▔▔▔▔▔ - """ - ) - ) + out.write(util_mod.underlined("active mounts:")) inode_info = stat_info.mountPointInfo for key in inode_info: diff --git a/eden/fs/cli/util.py b/eden/fs/cli/util.py index bd90935a3a..9435be6c35 100644 --- a/eden/fs/cli/util.py +++ b/eden/fs/cli/util.py @@ -669,6 +669,11 @@ def format_mount(mount) -> str: return os.fsdecode(os.path.basename(mount)) +def underlined(message: str) -> str: + line = "-" * len(message) + return f"\n{message}\n{line}\n" + + def is_edenfs_mount_device(device: bytes) -> bool: return device == b"edenfs" or device.startswith(b"edenfs:") diff --git a/eden/integration/rage_test.py b/eden/integration/rage_test.py index 4fe164c2e3..73136209ad 100644 --- a/eden/integration/rage_test.py +++ b/eden/integration/rage_test.py @@ -26,7 +26,7 @@ class RageTest(testcase.EdenRepoTest): self.assertRegex(output, r"\nbuild_package_version\s*:") self.assertRegex(output, r"\nuptime\s*:") self.assertIn(f"\nChecking {self.mount}\n", output) - self.assertIn("edenfs memory usage", output) + self.assertIn("EdenFS memory usage", output) def test_rage_output_with_stopped_daemon(self) -> None: self.eden.shutdown() @@ -41,7 +41,7 @@ class RageTest(testcase.EdenRepoTest): # We may need to update this in the future if we modify the rage output; the # main purpose it simply to make sure that the rage command did not exit early # or crash partway through the output. - self.assertRegex(output, r"^User\s*:") + self.assertRegex(output, r"\nUser\s*:") self.assertRegex(output, r"\nHostname\s*:") self.assertRegex(output, r"\nVersion\s*:") self.assertIn("\neden doctor --dry-run", output) @@ -49,4 +49,6 @@ class RageTest(testcase.EdenRepoTest): self.assertIn("\nList of running EdenFS processes:\n", output) self.assertIn("\nList of mount points:\n", output) self.assertIn(f"\nMount point info for path {self.mount}:\n", output) + self.assertIn("\nEnvironment variables:\n", output) + self.assertIn("\nEdenFS config:\n", output) self.assertIn(f"{self.mount}\n", output)