libutil: add a log line splitter
we'll need this in other places too soon. Change-Id: I8ddd57aefa4cbd1f8f7232f35e30a204ace77e58
This commit is contained in:
@@ -1229,27 +1229,20 @@ Goal::WorkResult DerivationGoal::tooMuchLogs()
|
||||
kj::Promise<Result<std::optional<Goal::WorkResult>>>
|
||||
DerivationGoal::handleBuilderOutput(AsyncInputStream & in) noexcept
|
||||
try {
|
||||
std::string currentLogLine;
|
||||
size_t currentLogLinePos = 0; // to handle carriage return
|
||||
LogLineSplitter splitter;
|
||||
|
||||
auto flushLine = [&] {
|
||||
KJ_DEFER({
|
||||
currentLogLine = "";
|
||||
currentLogLinePos = 0;
|
||||
});
|
||||
|
||||
if (const auto state = handleJSONLogMessage(
|
||||
currentLogLine, *act, builderActivities, "the derivation builder"
|
||||
))
|
||||
auto flushLine = [&](const std::string & line) {
|
||||
if (const auto state =
|
||||
handleJSONLogMessage(line, *act, builderActivities, "the derivation builder"))
|
||||
{
|
||||
return *state;
|
||||
} else {
|
||||
logTail.push_back(currentLogLine);
|
||||
logTail.push_back(line);
|
||||
if (logTail.size() > settings.logLines) {
|
||||
logTail.pop_front();
|
||||
}
|
||||
|
||||
return act->result(resBuildLogLine, currentLogLine);
|
||||
return act->result(resBuildLogLine, line);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1274,8 +1267,8 @@ try {
|
||||
lastChildActivity = AIO().provider.getTimer().now();
|
||||
|
||||
if (data.empty()) {
|
||||
if (!currentLogLine.empty()) {
|
||||
if (flushLine() == Logger::BufferState::NeedsFlush) {
|
||||
if (auto left = splitter.finish(); !left.empty()) {
|
||||
if (flushLine(left) == Logger::BufferState::NeedsFlush) {
|
||||
TRY_AWAIT(act->getLogger().flush());
|
||||
}
|
||||
}
|
||||
@@ -1287,20 +1280,17 @@ try {
|
||||
co_return tooMuchLogs();
|
||||
}
|
||||
|
||||
for (auto c : data)
|
||||
if (c == '\r')
|
||||
currentLogLinePos = 0;
|
||||
else if (c == '\n') {
|
||||
if (flushLine() == Logger::BufferState::NeedsFlush) {
|
||||
if (logSink) {
|
||||
(*logSink)(data);
|
||||
}
|
||||
|
||||
while (!data.empty()) {
|
||||
if (auto line = splitter.feed(data)) {
|
||||
if (flushLine(*line) == Logger::BufferState::NeedsFlush) {
|
||||
TRY_AWAIT(act->getLogger().flush());
|
||||
}
|
||||
} else {
|
||||
if (currentLogLinePos >= currentLogLine.size())
|
||||
currentLogLine.resize(currentLogLinePos + 1);
|
||||
currentLogLine[currentLogLinePos++] = c;
|
||||
}
|
||||
|
||||
if (logSink) (*logSink)(data);
|
||||
}
|
||||
}
|
||||
} catch (...) {
|
||||
co_return result::current_exception();
|
||||
|
||||
@@ -423,4 +423,28 @@ void logFatal(std::string const & s)
|
||||
syslog(LOG_CRIT, "%s", requireCString(s).asCStr());
|
||||
}
|
||||
|
||||
std::optional<std::string> LogLineSplitter::feed(std::string_view & input)
|
||||
{
|
||||
for (auto [idx, c] : enumerate(input)) {
|
||||
if (c == '\r') {
|
||||
pos = 0;
|
||||
} else if (c == '\n') {
|
||||
input = input.substr(idx + 1);
|
||||
return finish();
|
||||
} else {
|
||||
if (pos >= line.size()) {
|
||||
line.resize(pos + 1);
|
||||
}
|
||||
line[pos++] = c;
|
||||
}
|
||||
}
|
||||
input = {};
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::string LogLineSplitter::finish()
|
||||
{
|
||||
pos = 0;
|
||||
return std::move(line);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -435,4 +435,27 @@ std::optional<Logger::BufferState> handleJSONLogMessage(
|
||||
std::map<ActivityId, Activity> & activities,
|
||||
std::string_view source
|
||||
);
|
||||
|
||||
/**
|
||||
* Split a log stream into lines, processing carriage returns (`\r`) as a terminal would.
|
||||
*/
|
||||
class LogLineSplitter
|
||||
{
|
||||
std::string line;
|
||||
size_t pos = 0;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Feeds some input to the splitter and returns the first full line or `nullopt` if
|
||||
* there is no complete line in the buffer yet. If any input remains `input` is set
|
||||
* to the unconsumed data and `feed` should be called again until `input` is empty.
|
||||
* If this function returns `nullopt` it guarantees that `input` is fully consumed.
|
||||
*/
|
||||
std::optional<std::string> feed(std::string_view & input);
|
||||
|
||||
/**
|
||||
* Clear the line buffer and return its current contents.
|
||||
*/
|
||||
std::string finish();
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user