Cut off terminal after 3000 lines

When autoscroll was disabled, terminal could run on endlessly, eating
memory until the browser crashes. Now a hard upper limit ensures
that not more than 3000 lines are ever stored in the terminal. If autoscroll
is disabled and the limit is reached, no more log lines will be added. That
ensures that the log will not scroll and the current log excerpt will stay put.
This commit is contained in:
Gina Häußge 2015-11-27 15:05:55 +01:00
parent 043b213a96
commit 40fe74ef45
2 changed files with 33 additions and 9 deletions

View file

@ -7,6 +7,7 @@ $(function() {
self.log = ko.observableArray([]);
self.buffer = ko.observable(300);
self.upperLimit = ko.observable(3000);
self.command = ko.observable(undefined);
@ -52,13 +53,21 @@ $(function() {
self.lineCount = ko.computed(function() {
var total = self.log().length;
var filtered = _.filter(self.displayedLines(), function(entry) { return entry.type == "filtered" }).length;
var displayed = _.filter(self.displayedLines(), function(entry) { return entry.type == "line" }).length;
var filtered = total - displayed;
if (total == displayed) {
return _.sprintf(gettext("showing %(displayed)d lines"), {displayed: displayed});
if (filtered > 0) {
if (total > self.upperLimit()) {
return _.sprintf(gettext("showing %(displayed)d lines (%(filtered)d of %(total)d total lines filtered, buffer full)"), {displayed: displayed, total: total, filtered: filtered});
} else {
return _.sprintf(gettext("showing %(displayed)d lines (%(filtered)d of %(total)d total lines filtered)"), {displayed: displayed, total: total, filtered: filtered});
}
} else {
return _.sprintf(gettext("showing %(displayed)d lines (%(filtered)d of %(total)d total lines filtered)"), {displayed: displayed, total: total, filtered: filtered});
if (total > self.upperLimit()) {
return _.sprintf(gettext("showing %(displayed)d lines (buffer full)"), {displayed: displayed});
} else {
return _.sprintf(gettext("showing %(displayed)d lines"), {displayed: displayed});
}
}
});
@ -84,10 +93,25 @@ $(function() {
};
self._processCurrentLogData = function(data) {
self.log(self.log().concat(_.map(data, function(line) { return self._toInternalFormat(line) })));
if (self.autoscrollEnabled()) {
self.log(self.log.slice(-self.buffer()));
var length = self.log().length;
if (length >= self.upperLimit()) {
var cutoff = "--- too many lines to buffer, cut off ---";
var last = self.log()[length-1];
if (!last || last.type != "cut" || last.line != cutoff) {
self.log(self.log().concat(self._toInternalFormat(cutoff, "cut")));
}
return;
}
var newLog = self.log().concat(_.map(data, function(line) { return self._toInternalFormat(line) }));
if (self.autoscrollEnabled()) {
// we only keep the last <buffer> entries
newLog = newLog.slice(-self.buffer());
} else if (newLog.length > self.upperLimit()) {
// we only keep the first <upperLimit> entries
newLog = newLog.slice(0, self.upperLimit());
}
self.log(newLog);
};
self._processHistoryLogData = function(data) {
@ -232,4 +256,4 @@ $(function() {
["loginStateViewModel", "settingsViewModel"],
"#term"
]);
});
});

View file

@ -1,5 +1,5 @@
<div class="terminal">
<pre id="terminal-output" class="pre-scrollable" data-bind="foreach: displayedLines"><span data-bind="text: line, css: {muted: type == 'filtered'}"></span><br></pre>
<pre id="terminal-output" class="pre-scrollable" data-bind="foreach: displayedLines"><span data-bind="text: line, css: {muted: type == 'filtered' || type == 'cut'}"></span><br></pre>
<small class="pull-left"><button class="btn btn-mini" data-bind="click: toggleAutoscroll, css: {active: autoscrollEnabled}">{{ _('Autoscroll') }}</button> <span data-bind="text: lineCount"></span></small>
<small class="pull-right"><a href="#" data-bind="click: scrollToEnd">{{ _("Scroll to end") }}</a>&nbsp;|&nbsp;<a href="#" data-bind="click: selectAll">{{ _("Select all") }}</a></small>
</div>