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
This commit is contained in:
Genevieve Helsel 2021-09-10 23:53:45 -07:00 committed by Facebook GitHub Bot
parent bae2745023
commit 21c2410a6d
5 changed files with 42 additions and 48 deletions

View File

@ -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:

View File

@ -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())

View File

@ -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:

View File

@ -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:")

View File

@ -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)