From 61407c3e0699946312878273eb3d7a901c2ebb73 Mon Sep 17 00:00:00 2001 From: akwizgran Date: Mon, 8 Oct 2018 11:12:15 +0100 Subject: [PATCH] Save logs to disk. --- .../briar/android/AndroidComponent.java | 6 ++ .../briar/android/BriarApplicationImpl.java | 21 ++++- .../android/reporting/BriarReportPrimer.java | 24 ++++- .../api/logging/PersistentLogManager.java | 16 ++++ .../briarproject/briar/BriarCoreModule.java | 2 + .../briar}/logging/BriefLogFormatter.java | 3 +- .../briar/logging/LoggingModule.java | 23 +++++ .../logging/PersistentLogManagerImpl.java | 93 +++++++++++++++++++ 8 files changed, 183 insertions(+), 5 deletions(-) create mode 100644 briar-api/src/main/java/org/briarproject/briar/api/logging/PersistentLogManager.java rename {briar-android/src/main/java/org/briarproject/briar/android => briar-core/src/main/java/org/briarproject/briar}/logging/BriefLogFormatter.java (96%) create mode 100644 briar-core/src/main/java/org/briarproject/briar/logging/LoggingModule.java create mode 100644 briar-core/src/main/java/org/briarproject/briar/logging/PersistentLogManagerImpl.java diff --git a/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java b/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java index 5a9092582..de68a6358 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/AndroidComponent.java @@ -46,6 +46,7 @@ import org.briarproject.briar.api.feed.FeedManager; import org.briarproject.briar.api.forum.ForumManager; import org.briarproject.briar.api.forum.ForumSharingManager; import org.briarproject.briar.api.introduction.IntroductionManager; +import org.briarproject.briar.api.logging.PersistentLogManager; import org.briarproject.briar.api.messaging.MessagingManager; import org.briarproject.briar.api.messaging.PrivateMessageFactory; import org.briarproject.briar.api.privategroup.GroupMessageFactory; @@ -56,6 +57,7 @@ import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager import org.briarproject.briar.api.test.TestDataCreator; import java.util.concurrent.Executor; +import java.util.logging.Formatter; import javax.inject.Singleton; @@ -165,6 +167,10 @@ public interface AndroidComponent FeatureFlags featureFlags(); + PersistentLogManager persistentLogManager(); + + Formatter formatter(); + void inject(SignInReminderReceiver briarService); void inject(BriarService briarService); diff --git a/briar-android/src/main/java/org/briarproject/briar/android/BriarApplicationImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/BriarApplicationImpl.java index 493a542a7..7ad4d4739 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/BriarApplicationImpl.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/BriarApplicationImpl.java @@ -27,8 +27,12 @@ import org.briarproject.briar.android.reporting.BriarReportPrimer; import org.briarproject.briar.android.reporting.BriarReportSenderFactory; import org.briarproject.briar.android.reporting.DevReportActivity; import org.briarproject.briar.android.util.UiUtils; +import org.briarproject.briar.api.logging.PersistentLogManager; +import java.io.File; +import java.io.IOException; import java.util.Collection; +import java.util.logging.Formatter; import java.util.logging.Handler; import java.util.logging.LogRecord; import java.util.logging.Logger; @@ -36,6 +40,7 @@ import java.util.logging.Logger; import static android.app.ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND; import static java.util.logging.Level.FINE; import static java.util.logging.Level.INFO; +import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger; import static org.acra.ReportField.ANDROID_VERSION; import static org.acra.ReportField.APP_VERSION_CODE; @@ -54,6 +59,7 @@ import static org.acra.ReportField.REPORT_ID; import static org.acra.ReportField.STACK_TRACE; import static org.acra.ReportField.USER_APP_START_DATE; import static org.acra.ReportField.USER_CRASH_DATE; +import static org.briarproject.bramble.util.LogUtils.logException; import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD; @ReportsCrashes( @@ -120,9 +126,22 @@ public class BriarApplicationImpl extends Application rootLogger.addHandler(logHandler); rootLogger.setLevel(IS_DEBUG_BUILD ? FINE : INFO); + applicationComponent = createApplicationComponent(); + + PersistentLogManager logManager = + applicationComponent.persistentLogManager(); + Formatter formatter = applicationComponent.formatter(); + try { + File logDir = getDir("log", MODE_PRIVATE); + Handler handler = logManager.createLogHandler(logDir); + handler.setFormatter(formatter); + rootLogger.addHandler(handler); + } catch (IOException e) { + logException(LOG, WARNING, e); + } + LOG.info("Created"); - applicationComponent = createApplicationComponent(); EmojiManager.install(new GoogleEmojiProvider()); } diff --git a/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportPrimer.java b/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportPrimer.java index 8c5e3e6e3..e87051ed6 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportPrimer.java +++ b/briar-android/src/main/java/org/briarproject/briar/android/reporting/BriarReportPrimer.java @@ -15,10 +15,12 @@ import org.acra.builder.ReportBuilder; import org.acra.builder.ReportPrimer; import org.briarproject.bramble.api.Pair; import org.briarproject.briar.BuildConfig; +import org.briarproject.briar.android.AndroidComponent; import org.briarproject.briar.android.BriarApplication; -import org.briarproject.briar.android.logging.BriefLogFormatter; +import org.briarproject.briar.api.logging.PersistentLogManager; import java.io.File; +import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.net.InetAddress; @@ -37,6 +39,7 @@ import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE; import static android.bluetooth.BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE; import static android.content.Context.ACTIVITY_SERVICE; import static android.content.Context.CONNECTIVITY_SERVICE; +import static android.content.Context.MODE_PRIVATE; import static android.content.Context.WIFI_P2P_SERVICE; import static android.content.Context.WIFI_SERVICE; import static android.net.ConnectivityManager.TYPE_MOBILE; @@ -81,13 +84,28 @@ public class BriarReportPrimer implements ReportPrimer { // Log BriarApplication app = (BriarApplication) ctx.getApplicationContext(); + AndroidComponent appComponent = app.getApplicationComponent(); + PersistentLogManager logManager = + appComponent.persistentLogManager(); + Formatter formatter = appComponent.formatter(); + StringBuilder sb = new StringBuilder(); - Formatter formatter = new BriefLogFormatter(); for (LogRecord record : app.getRecentLogRecords()) { - sb.append(formatter.format(record)).append('\n'); + sb.append(formatter.format(record)); } customData.put("Log", sb.toString()); + sb = new StringBuilder(); + try { + File logDir = ctx.getDir("log", MODE_PRIVATE); + for (String line : logManager.getPersistedLog(logDir)) { + sb.append(line).append('\n'); + } + } catch (IOException e) { + sb.append("Could not recover persisted log: ").append(e); + } + customData.put("Persisted log", sb.toString()); + // System memory Object o = ctx.getSystemService(ACTIVITY_SERVICE); ActivityManager am = (ActivityManager) o; diff --git a/briar-api/src/main/java/org/briarproject/briar/api/logging/PersistentLogManager.java b/briar-api/src/main/java/org/briarproject/briar/api/logging/PersistentLogManager.java new file mode 100644 index 000000000..661454527 --- /dev/null +++ b/briar-api/src/main/java/org/briarproject/briar/api/logging/PersistentLogManager.java @@ -0,0 +1,16 @@ +package org.briarproject.briar.api.logging; + +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; + +import java.io.File; +import java.io.IOException; +import java.util.Collection; +import java.util.logging.Handler; + +@NotNullByDefault +public interface PersistentLogManager { + + Handler createLogHandler(File dir) throws IOException; + + Collection getPersistedLog(File dir) throws IOException; +} diff --git a/briar-core/src/main/java/org/briarproject/briar/BriarCoreModule.java b/briar-core/src/main/java/org/briarproject/briar/BriarCoreModule.java index 5ba057706..c4cb380c7 100644 --- a/briar-core/src/main/java/org/briarproject/briar/BriarCoreModule.java +++ b/briar-core/src/main/java/org/briarproject/briar/BriarCoreModule.java @@ -6,6 +6,7 @@ import org.briarproject.briar.feed.DnsModule; import org.briarproject.briar.feed.FeedModule; import org.briarproject.briar.forum.ForumModule; import org.briarproject.briar.introduction.IntroductionModule; +import org.briarproject.briar.logging.LoggingModule; import org.briarproject.briar.messaging.MessagingModule; import org.briarproject.briar.privategroup.PrivateGroupModule; import org.briarproject.briar.privategroup.invitation.GroupInvitationModule; @@ -22,6 +23,7 @@ import dagger.Module; ForumModule.class, GroupInvitationModule.class, IntroductionModule.class, + LoggingModule.class, MessagingModule.class, PrivateGroupModule.class, SharingModule.class, diff --git a/briar-android/src/main/java/org/briarproject/briar/android/logging/BriefLogFormatter.java b/briar-core/src/main/java/org/briarproject/briar/logging/BriefLogFormatter.java similarity index 96% rename from briar-android/src/main/java/org/briarproject/briar/android/logging/BriefLogFormatter.java rename to briar-core/src/main/java/org/briarproject/briar/logging/BriefLogFormatter.java index 00906c93e..fae52b378 100644 --- a/briar-android/src/main/java/org/briarproject/briar/android/logging/BriefLogFormatter.java +++ b/briar-core/src/main/java/org/briarproject/briar/logging/BriefLogFormatter.java @@ -1,4 +1,4 @@ -package org.briarproject.briar.android.logging; +package org.briarproject.briar.logging; import org.briarproject.bramble.api.nullsafety.NotNullByDefault; @@ -47,6 +47,7 @@ public class BriefLogFormatter extends Formatter { sb.append('\n'); appendThrowable(sb, t); } + sb.append('\n'); return sb.toString(); } diff --git a/briar-core/src/main/java/org/briarproject/briar/logging/LoggingModule.java b/briar-core/src/main/java/org/briarproject/briar/logging/LoggingModule.java new file mode 100644 index 000000000..7d69cf188 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/logging/LoggingModule.java @@ -0,0 +1,23 @@ +package org.briarproject.briar.logging; + +import org.briarproject.briar.api.logging.PersistentLogManager; + +import java.util.logging.Formatter; + +import dagger.Module; +import dagger.Provides; + +@Module +public class LoggingModule { + + @Provides + Formatter provideFormatter() { + return new BriefLogFormatter(); + } + + @Provides + PersistentLogManager providePersistentLogManager( + PersistentLogManagerImpl logManager) { + return logManager; + } +} diff --git a/briar-core/src/main/java/org/briarproject/briar/logging/PersistentLogManagerImpl.java b/briar-core/src/main/java/org/briarproject/briar/logging/PersistentLogManagerImpl.java new file mode 100644 index 000000000..bbbed14e3 --- /dev/null +++ b/briar-core/src/main/java/org/briarproject/briar/logging/PersistentLogManagerImpl.java @@ -0,0 +1,93 @@ +package org.briarproject.briar.logging; + +import org.briarproject.bramble.api.lifecycle.IoExecutor; +import org.briarproject.bramble.api.lifecycle.ShutdownManager; +import org.briarproject.bramble.api.nullsafety.NotNullByDefault; +import org.briarproject.bramble.api.system.Scheduler; +import org.briarproject.briar.api.logging.PersistentLogManager; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Scanner; +import java.util.concurrent.Executor; +import java.util.concurrent.ScheduledExecutorService; +import java.util.logging.Formatter; +import java.util.logging.Handler; +import java.util.logging.Logger; +import java.util.logging.StreamHandler; + +import javax.annotation.concurrent.ThreadSafe; +import javax.inject.Inject; + +import static java.util.Collections.emptyList; +import static java.util.concurrent.TimeUnit.MILLISECONDS; +import static java.util.concurrent.TimeUnit.MINUTES; + +@ThreadSafe +@NotNullByDefault +class PersistentLogManagerImpl implements PersistentLogManager { + + private static final Logger LOG = + Logger.getLogger(PersistentLogManagerImpl.class.getName()); + + private static final String LOG_FILE = "briar.log"; + private static final String OLD_LOG_FILE = "briar.log.old"; + private static final long FLUSH_INTERVAL_MS = MINUTES.toMillis(5); + + private final ScheduledExecutorService scheduler; + private final Executor ioExecutor; + private final ShutdownManager shutdownManager; + private final Formatter formatter; + + @Inject + PersistentLogManagerImpl(@Scheduler ScheduledExecutorService scheduler, + @IoExecutor Executor ioExecutor, ShutdownManager shutdownManager, + Formatter formatter) { + this.scheduler = scheduler; + this.ioExecutor = ioExecutor; + this.shutdownManager = shutdownManager; + this.formatter = formatter; + } + + @Override + public Handler createLogHandler(File dir) throws IOException { + File logFile = new File(dir, LOG_FILE); + File oldLogFile = new File(dir, OLD_LOG_FILE); + if (oldLogFile.exists() && !oldLogFile.delete()) + LOG.warning("Failed to delete old log file"); + if (logFile.exists() && !logFile.renameTo(oldLogFile)) + LOG.warning("Failed to rename log file"); + try { + OutputStream out = new FileOutputStream(logFile); + StreamHandler handler = new StreamHandler(out, formatter); + scheduler.scheduleWithFixedDelay(() -> + ioExecutor.execute(handler::flush), + FLUSH_INTERVAL_MS, FLUSH_INTERVAL_MS, MILLISECONDS); + shutdownManager.addShutdownHook(handler::flush); + return handler; + } catch (SecurityException e) { + throw new IOException(e); + } + } + + @Override + public Collection getPersistedLog(File dir) throws IOException { + File oldLogFile = new File(dir, OLD_LOG_FILE); + if (oldLogFile.exists()) { + LOG.info("Reading old log file"); + List lines = new ArrayList<>(); + Scanner s = new Scanner(oldLogFile); + while (s.hasNextLine()) lines.add(s.nextLine()); + s.close(); + return lines; + } else { + LOG.info("Old log file does not exist"); + return emptyList(); + } + } +}