mirror of
https://github.com/facebook/sapling.git
synced 2024-10-07 15:27:13 +03:00
live debug journal
command
Summary: Running `eden debug journal -f` will print and follow the eden journal in a similar style to the unix `tail -f` command. Reviewed By: chadaustin Differential Revision: D16112458 fbshipit-source-id: 5304cd0f857bdbeca41c2591e98920f4f1fc8f42
This commit is contained in:
parent
969abcdcb4
commit
e2d4362896
@ -15,6 +15,7 @@ import shlex
|
||||
import shutil
|
||||
import stat
|
||||
import sys
|
||||
import time
|
||||
from pathlib import Path
|
||||
from typing import (
|
||||
IO,
|
||||
@ -884,7 +885,7 @@ class DebugJournalGetMemoryLimitCmd(Subcmd):
|
||||
return 0
|
||||
|
||||
|
||||
@debug_cmd("journal", "Prints the most recent N entries from the journal")
|
||||
@debug_cmd("journal", "Prints the most recent entries from the journal")
|
||||
class DebugJournalCmd(Subcmd):
|
||||
def setup_parser(self, parser: argparse.ArgumentParser) -> None:
|
||||
parser.add_argument(
|
||||
@ -901,6 +902,13 @@ class DebugJournalCmd(Subcmd):
|
||||
help="Show only deltas for paths matching this pattern. "
|
||||
"Specify '^((?!^\\.hg/).)*$' to exclude the .hg/ directory.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-f",
|
||||
"--follow",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Output appended data as the journal grows.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-i",
|
||||
"--ignore-case",
|
||||
@ -915,27 +923,48 @@ class DebugJournalCmd(Subcmd):
|
||||
)
|
||||
|
||||
def run(self, args: argparse.Namespace) -> int:
|
||||
instance, checkout, _rel_path = cmd_util.require_checkout(args, args.path)
|
||||
pattern: Optional[Pattern[bytes]] = None
|
||||
if args.pattern:
|
||||
pattern_bytes = args.pattern.encode("utf-8")
|
||||
flags = re.IGNORECASE if args.ignore_case else 0
|
||||
pattern = re.compile(pattern_bytes, flags)
|
||||
|
||||
with instance.get_thrift_client() as client:
|
||||
params = DebugGetRawJournalParams(
|
||||
mountPoint=bytes(checkout.path), limit=args.limit
|
||||
)
|
||||
try:
|
||||
raw_journal = client.debugGetRawJournal(params)
|
||||
except EdenError as err:
|
||||
print(err, file=sys.stderr)
|
||||
return 1
|
||||
if args.pattern:
|
||||
flags = re.IGNORECASE if args.ignore_case else 0
|
||||
bytes_pattern = args.pattern.encode("utf-8")
|
||||
pattern: Optional[Pattern[bytes]] = re.compile(bytes_pattern, flags)
|
||||
instance, checkout, _ = cmd_util.require_checkout(args, args.path)
|
||||
mount = bytes(checkout.path)
|
||||
|
||||
def refresh(params):
|
||||
with instance.get_thrift_client() as client:
|
||||
journal = client.debugGetRawJournal(params)
|
||||
|
||||
deltas = journal.allDeltas
|
||||
if len(deltas) == 0:
|
||||
seq_num = params.fromSequenceNumber
|
||||
else:
|
||||
pattern = None
|
||||
# debugGetRawJournal() returns the most recent entries first, but
|
||||
# we want to display the oldest entries first, so we pass a reversed
|
||||
# iterator along.
|
||||
_print_raw_journal_deltas(reversed(raw_journal.allDeltas), pattern)
|
||||
seq_num = deltas[0].fromPosition.sequenceNumber + 1
|
||||
_print_raw_journal_deltas(reversed(deltas), pattern)
|
||||
|
||||
return seq_num
|
||||
|
||||
try:
|
||||
params = DebugGetRawJournalParams(
|
||||
mountPoint=mount, fromSequenceNumber=1, limit=args.limit
|
||||
)
|
||||
seq_num = refresh(params)
|
||||
while args.follow:
|
||||
REFRESH_SEC = 2
|
||||
time.sleep(REFRESH_SEC)
|
||||
params = DebugGetRawJournalParams(
|
||||
mountPoint=mount, fromSequenceNumber=seq_num
|
||||
)
|
||||
seq_num = refresh(params)
|
||||
except EdenError as err:
|
||||
print(err, file=sys.stderr)
|
||||
return 1
|
||||
except KeyboardInterrupt:
|
||||
if args.follow:
|
||||
pass
|
||||
else:
|
||||
raise
|
||||
|
||||
return 0
|
||||
|
||||
|
@ -224,12 +224,13 @@ std::unique_ptr<JournalDeltaRange> Journal::accumulateRange(
|
||||
}
|
||||
|
||||
std::vector<DebugJournalDelta> Journal::getDebugRawJournalInfo(
|
||||
size_t limit,
|
||||
SequenceNumber from,
|
||||
std::optional<size_t> limit,
|
||||
long mountGeneration) const {
|
||||
auto result = std::vector<DebugJournalDelta>();
|
||||
forEachDelta(
|
||||
0,
|
||||
std::optional<size_t>(limit),
|
||||
from,
|
||||
limit,
|
||||
[mountGeneration, &result](const JournalDelta& current) -> void {
|
||||
DebugJournalDelta delta;
|
||||
JournalPosition fromPosition;
|
||||
|
@ -144,7 +144,8 @@ class Journal {
|
||||
* reached then it will just return what had been currently found.
|
||||
* */
|
||||
std::vector<DebugJournalDelta> getDebugRawJournalInfo(
|
||||
size_t limit,
|
||||
SequenceNumber from,
|
||||
std::optional<size_t> limit,
|
||||
long mountGeneration) const;
|
||||
|
||||
void setMemoryLimit(size_t limit);
|
||||
|
@ -138,7 +138,7 @@ TEST(Journal, debugRawJournalInfoRemoveCreateUpdate) {
|
||||
|
||||
long mountGen = 333;
|
||||
|
||||
auto debugDeltas = journal.getDebugRawJournalInfo(3, mountGen);
|
||||
auto debugDeltas = journal.getDebugRawJournalInfo(0, 3, mountGen);
|
||||
ASSERT_EQ(3, debugDeltas.size());
|
||||
|
||||
// Debug Raw Journal Info returns info from newest->latest
|
||||
@ -155,14 +155,14 @@ TEST(Journal, debugRawJournalInfoRemoveCreateUpdate) {
|
||||
EXPECT_EQ(debugDeltas[2].fromPosition.mountGeneration, mountGen);
|
||||
EXPECT_EQ(debugDeltas[2].fromPosition.sequenceNumber, 1);
|
||||
|
||||
debugDeltas = journal.getDebugRawJournalInfo(1, mountGen);
|
||||
debugDeltas = journal.getDebugRawJournalInfo(0, 1, mountGen);
|
||||
ASSERT_EQ(1, debugDeltas.size());
|
||||
EXPECT_TRUE(debugDeltas[0].changedPaths["test.txt"].existedBefore);
|
||||
EXPECT_TRUE(debugDeltas[0].changedPaths["test.txt"].existedAfter);
|
||||
EXPECT_EQ(debugDeltas[0].fromPosition.mountGeneration, mountGen);
|
||||
EXPECT_EQ(debugDeltas[0].fromPosition.sequenceNumber, 3);
|
||||
|
||||
debugDeltas = journal.getDebugRawJournalInfo(0, mountGen);
|
||||
debugDeltas = journal.getDebugRawJournalInfo(0, 0, mountGen);
|
||||
ASSERT_EQ(0, debugDeltas.size());
|
||||
}
|
||||
|
||||
|
@ -675,8 +675,14 @@ void EdenServiceHandler::debugGetRawJournal(
|
||||
auto helper = INSTRUMENT_THRIFT_CALL(DBG3, params->mountPoint);
|
||||
auto edenMount = server_->getMount(params->mountPoint);
|
||||
auto mountGeneration = static_cast<ssize_t>(edenMount->getMountGeneration());
|
||||
|
||||
std::optional<size_t> limitopt = std::nullopt;
|
||||
if (auto limit = params->limit_ref()) {
|
||||
limitopt = static_cast<size_t>(*limit);
|
||||
}
|
||||
|
||||
out.allDeltas = edenMount->getJournal().getDebugRawJournalInfo(
|
||||
static_cast<size_t>(params->limit), mountGeneration);
|
||||
params->fromSequenceNumber, limitopt, mountGeneration);
|
||||
#else
|
||||
NOT_IMPLEMENTED();
|
||||
#endif // !_WIN32
|
||||
|
@ -200,7 +200,8 @@ struct FileDelta {
|
||||
|
||||
struct DebugGetRawJournalParams {
|
||||
1: PathString mountPoint
|
||||
2: i32 limit
|
||||
2: optional i32 limit
|
||||
3: i32 fromSequenceNumber
|
||||
}
|
||||
|
||||
struct DebugPathChangeInfo {
|
||||
|
Loading…
Reference in New Issue
Block a user