Avoid creating an in-memory copy of the log where possible.

This helps to avoid OOMs on low-memory devices.
This commit is contained in:
akwizgran
2020-07-20 16:46:31 +01:00
parent a83682d4b6
commit 9ea4463cbc
4 changed files with 35 additions and 37 deletions

View File

@@ -5,7 +5,7 @@ import org.briarproject.bramble.api.settings.Settings;
import java.io.File; import java.io.File;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.Scanner;
import java.util.logging.Handler; import java.util.logging.Handler;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -36,11 +36,11 @@ public interface PersistentLogManager {
void addLogHandler(File dir, Logger logger) throws IOException; void addLogHandler(File dir, Logger logger) throws IOException;
/** /**
* Loads and returns the persistent log entries stored in the given * Returns a {@link Scanner} for reading the persistent log entries stored
* directory, or an empty list if no log entries are found. * in the given directory.
* *
* @param old True if the previous session's log should be loaded, or false * @param old True if the previous session's log should be loaded, or false
* if the current session's log should be loaded * if the current session's log should be loaded
*/ */
List<String> getPersistedLog(File dir, boolean old) throws IOException; Scanner getPersistedLog(File dir, boolean old) throws IOException;
} }

View File

@@ -16,14 +16,13 @@ import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriter; import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.api.transport.StreamWriterFactory; import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.ByteArrayInputStream;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.util.LinkedList;
import java.util.List;
import java.util.Scanner; import java.util.Scanner;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
@@ -37,7 +36,6 @@ import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe; import javax.annotation.concurrent.ThreadSafe;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.Collections.emptyList;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.bramble.util.LogUtils.logException;
@@ -52,7 +50,6 @@ class PersistentLogManagerImpl implements PersistentLogManager,
private static final String LOG_FILE = "briar.log"; private static final String LOG_FILE = "briar.log";
private static final String OLD_LOG_FILE = "briar.log.old"; private static final String OLD_LOG_FILE = "briar.log.old";
private static final int MAX_LINES_TO_RETURN = 10_000;
private final ScheduledExecutorService scheduler; private final ScheduledExecutorService scheduler;
private final Executor ioExecutor; private final Executor ioExecutor;
@@ -147,13 +144,13 @@ class PersistentLogManagerImpl implements PersistentLogManager,
} }
@Override @Override
public List<String> getPersistedLog(File dir, boolean old) public Scanner getPersistedLog(File dir, boolean old)
throws IOException { throws IOException {
if (old) { if (old) {
SecretKey oldLogKey = this.oldLogKey; SecretKey oldLogKey = this.oldLogKey;
if (oldLogKey == null) { if (oldLogKey == null) {
LOG.info("Old log key has not been loaded"); LOG.info("Old log key has not been loaded");
return emptyList(); return emptyScanner();
} }
return getPersistedLog(new File(dir, OLD_LOG_FILE), oldLogKey); return getPersistedLog(new File(dir, OLD_LOG_FILE), oldLogKey);
} else { } else {
@@ -161,31 +158,20 @@ class PersistentLogManagerImpl implements PersistentLogManager,
} }
} }
private List<String> getPersistedLog(File logFile, SecretKey key) private Scanner getPersistedLog(File logFile, SecretKey key)
throws IOException { throws IOException {
if (logFile.exists()) { if (logFile.exists()) {
LOG.info("Reading log file"); LOG.info("Reading log file");
LinkedList<String> lines = new LinkedList<>();
int numLines = 0;
InputStream in = new FileInputStream(logFile); InputStream in = new FileInputStream(logFile);
//noinspection TryFinallyCanBeTryWithResources return new Scanner(streamReaderFactory.createLogStreamReader(in,
try { key));
InputStream reader = streamReaderFactory
.createLogStreamReader(in, key);
Scanner s = new Scanner(reader);
while (s.hasNextLine()) {
lines.add(s.nextLine());
if (numLines == MAX_LINES_TO_RETURN) lines.poll();
else numLines++;
}
s.close();
return lines;
} finally {
in.close();
}
} else { } else {
LOG.info("Log file does not exist"); LOG.info("Log file does not exist");
return emptyList(); return emptyScanner();
} }
} }
private Scanner emptyScanner() {
return new Scanner(new ByteArrayInputStream(new byte[0]));
}
} }

View File

@@ -26,7 +26,9 @@ import java.lang.reflect.Method;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.Map; import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask; import java.util.concurrent.FutureTask;
@@ -54,6 +56,8 @@ import static org.briarproject.bramble.util.StringUtils.isNullOrEmpty;
public class BriarReportPrimer implements ReportPrimer { public class BriarReportPrimer implements ReportPrimer {
private static final int MAX_PERSISTED_LOG_LINES = 1_000;
@Override @Override
public void primeReport(@NonNull Context ctx, public void primeReport(@NonNull Context ctx,
@NonNull ReportBuilder builder) { @NonNull ReportBuilder builder) {
@@ -271,9 +275,16 @@ public class BriarReportPrimer implements ReportPrimer {
File logDir = ctx.getDir("log", MODE_PRIVATE); File logDir = ctx.getDir("log", MODE_PRIVATE);
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
try { try {
for (String line : logManager.getPersistedLog(logDir, old)) { Scanner scanner = logManager.getPersistedLog(logDir, old);
sb.append(line).append('\n'); LinkedList<String> lines = new LinkedList<>();
int numLines = 0;
while (scanner.hasNextLine()) {
lines.add(scanner.nextLine());
if (numLines == MAX_PERSISTED_LOG_LINES) lines.pollFirst();
else numLines++;
} }
scanner.close();
for (String line : lines) sb.append(line).append('\n');
} catch (IOException e) { } catch (IOException e) {
sb.append("Could not recover persisted log: ").append(e); sb.append("Could not recover persisted log: ").append(e);
} }

View File

@@ -19,7 +19,7 @@ import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.util.List; import java.util.Scanner;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -106,18 +106,19 @@ public class SettingsActivity extends BriarActivity {
ioExecutor.execute(() -> { ioExecutor.execute(() -> {
try { try {
File logDir = getApplication().getDir("log", MODE_PRIVATE); File logDir = getApplication().getDir("log", MODE_PRIVATE);
List<String> lines = logManager.getPersistedLog(logDir, old); Scanner scanner = logManager.getPersistedLog(logDir, old);
if (lines.isEmpty()) { if (!scanner.hasNextLine()) {
scanner.close();
runOnUiThreadUnlessDestroyed(() -> runOnUiThreadUnlessDestroyed(() ->
Toast.makeText(getApplication(), "Log is empty", Toast.makeText(getApplication(), "Log is empty",
LENGTH_LONG).show()); LENGTH_LONG).show());
return; return;
} }
PrintWriter w = new PrintWriter(osp.getOutputStream()); PrintWriter w = new PrintWriter(osp.getOutputStream());
for (String line : lines) { while (scanner.hasNextLine()) w.println(scanner.nextLine());
w.println(line); w.flush();
}
w.close(); w.close();
scanner.close();
runOnUiThreadUnlessDestroyed(() -> runOnUiThreadUnlessDestroyed(() ->
Toast.makeText(getApplication(), "Log exported", Toast.makeText(getApplication(), "Log exported",
LENGTH_LONG).show()); LENGTH_LONG).show());