From 989a0cc1bea686de198a742a7913ea0375c66c29 Mon Sep 17 00:00:00 2001 From: pacien Date: Wed, 14 Mar 2018 17:19:00 +0100 Subject: Implement dual logging for application context --- app/build.gradle | 2 + app/proguard-rules.pro | 4 ++ .../main/java/org/pacien/tincapp/context/App.kt | 18 ++++++++- .../java/org/pacien/tincapp/context/AppLogger.kt | 45 ++++++++++++++++++++++ .../java/org/pacien/tincapp/context/AppPaths.kt | 24 ++++++------ .../org/pacien/tincapp/service/TincVpnService.kt | 25 +++++++----- 6 files changed, 97 insertions(+), 21 deletions(-) create mode 100644 app/src/main/java/org/pacien/tincapp/context/AppLogger.kt diff --git a/app/build.gradle b/app/build.gradle index 8d418d0..53cafe8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -58,6 +58,8 @@ dependencies { compile 'org.bouncycastle:bcpkix-jdk15on:1.57' compile 'net.sourceforge.streamsupport:streamsupport-cfuture:1.5.5' + compile 'org.slf4j:slf4j-api:1.7.25' + compile 'com.github.tony19:logback-android:1.1.1-9' compile "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" } diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 96cfee0..ce89eec 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -1,5 +1,9 @@ -keep class org.bouncycastle.** +-keep class ch.qos.** { *; } +-keep class org.slf4j.** { *; } +-keepattributes *Annotation* -dontwarn org.apache.commons.** -dontwarn org.bouncycastle.** +-dontwarn ch.qos.logback.core.net.* -dontwarn sun.misc.Unsafe -dontwarn build.IgnoreJava8API diff --git a/app/src/main/java/org/pacien/tincapp/context/App.kt b/app/src/main/java/org/pacien/tincapp/context/App.kt index d79ae3e..53049f3 100644 --- a/app/src/main/java/org/pacien/tincapp/context/App.kt +++ b/app/src/main/java/org/pacien/tincapp/context/App.kt @@ -9,15 +9,31 @@ import android.support.annotation.StringRes import android.support.v7.app.AlertDialog import android.view.WindowManager import org.pacien.tincapp.R +import org.slf4j.Logger +import org.slf4j.LoggerFactory /** * @author pacien */ -class App : Application() { +class App : Application(), Thread.UncaughtExceptionHandler { + private var logger: Logger? = null + private var systemCrashHandler: Thread.UncaughtExceptionHandler? = null + override fun onCreate() { super.onCreate() appContext = applicationContext handler = Handler() + + AppLogger.configure() + logger = LoggerFactory.getLogger(this.javaClass) + + systemCrashHandler = Thread.getDefaultUncaughtExceptionHandler() + Thread.setDefaultUncaughtExceptionHandler(this) + } + + override fun uncaughtException(thread: Thread, throwable: Throwable) { + logger?.error("Fatal application error.", throwable) + systemCrashHandler?.uncaughtException(thread, throwable) } companion object { diff --git a/app/src/main/java/org/pacien/tincapp/context/AppLogger.kt b/app/src/main/java/org/pacien/tincapp/context/AppLogger.kt new file mode 100644 index 0000000..e240e70 --- /dev/null +++ b/app/src/main/java/org/pacien/tincapp/context/AppLogger.kt @@ -0,0 +1,45 @@ +package org.pacien.tincapp.context + +import ch.qos.logback.classic.Logger +import ch.qos.logback.classic.LoggerContext +import ch.qos.logback.classic.android.LogcatAppender +import ch.qos.logback.classic.encoder.PatternLayoutEncoder +import ch.qos.logback.classic.spi.ILoggingEvent +import ch.qos.logback.core.Context +import ch.qos.logback.core.FileAppender +import org.slf4j.LoggerFactory + +/** + * @author pacien + */ +object AppLogger { + private const val LOGCAT_PATTERN = "[%thread] %msg%n%rEx" + private const val LOGFILE_PATTERN = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n%rEx" + + fun configure() { + (LoggerFactory.getILoggerFactory() as LoggerContext) + .apply { reset() } + .let { loggerContext -> + (LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME) as Logger) + .apply { + addAppender(LogcatAppender() + .apply { context = loggerContext } + .apply { encoder = patternEncoder(loggerContext, LOGCAT_PATTERN) } + .apply { start() }) + } + .apply { + addAppender(FileAppender() + .apply { context = loggerContext } + .apply { encoder = patternEncoder(loggerContext, LOGFILE_PATTERN) } + .apply { file = AppPaths.appLogFile().absolutePath } + .apply { start() }) + } + } + } + + private fun patternEncoder(ctx: Context, pat: String) = + PatternLayoutEncoder() + .apply { context = ctx } + .apply { pattern = pat } + .apply { start() } +} diff --git a/app/src/main/java/org/pacien/tincapp/context/AppPaths.kt b/app/src/main/java/org/pacien/tincapp/context/AppPaths.kt index 9315060..4b36dfe 100644 --- a/app/src/main/java/org/pacien/tincapp/context/AppPaths.kt +++ b/app/src/main/java/org/pacien/tincapp/context/AppPaths.kt @@ -10,21 +10,22 @@ import java.io.FileNotFoundException * @implNote Logs and PID files are stored in the cache directory for easy clean up. */ object AppPaths { - private val TINCD_BIN = "libtincd.so" - private val TINC_BIN = "libtinc.so" + private const val TINCD_BIN = "libtincd.so" + private const val TINC_BIN = "libtinc.so" - private val LOGFILE_FORMAT = "tinc.%s.log" - private val PIDFILE_FORMAT = "tinc.%s.pid" + private const val APPLOG_FILE = "tincapp.log" + private const val LOGFILE_FORMAT = "tinc.%s.log" + private const val PIDFILE_FORMAT = "tinc.%s.pid" - private val NET_CONF_FILE = "network.conf" - private val NET_TINC_CONF_FILE = "tinc.conf" - private val NET_HOSTS_DIR = "hosts" - private val NET_INVITATION_FILE = "invitation-data" - private val NET_DEFAULT_ED25519_PRIVATE_KEY_FILE = "ed25519_key.priv" - private val NET_DEFAULT_RSA_PRIVATE_KEY_FILE = "rsa_key.priv" + private const val NET_CONF_FILE = "network.conf" + private const val NET_TINC_CONF_FILE = "tinc.conf" + private const val NET_HOSTS_DIR = "hosts" + private const val NET_INVITATION_FILE = "invitation-data" + private const val NET_DEFAULT_ED25519_PRIVATE_KEY_FILE = "ed25519_key.priv" + private const val NET_DEFAULT_RSA_PRIVATE_KEY_FILE = "rsa_key.priv" fun storageAvailable() = - Environment.getExternalStorageState().let { it == Environment.MEDIA_MOUNTED && it != Environment.MEDIA_MOUNTED_READ_ONLY } + Environment.getExternalStorageState().let { it == Environment.MEDIA_MOUNTED && it != Environment.MEDIA_MOUNTED_READ_ONLY } fun cacheDir() = App.getContext().externalCacheDir fun confDir() = App.getContext().getExternalFilesDir(null) @@ -37,6 +38,7 @@ object AppPaths { fun invitationFile(netName: String) = File(confDir(netName), NET_INVITATION_FILE) fun logFile(netName: String) = File(cacheDir(), String.format(LOGFILE_FORMAT, netName)) fun pidFile(netName: String) = File(App.getContext().cacheDir, String.format(PIDFILE_FORMAT, netName)) + fun appLogFile() = File(cacheDir(), APPLOG_FILE) fun existing(f: File) = f.apply { if (!exists()) throw FileNotFoundException(f.absolutePath) } diff --git a/app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt b/app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt index 0bf72a1..69811f4 100644 --- a/app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt +++ b/app/src/main/java/org/pacien/tincapp/service/TincVpnService.kt @@ -5,7 +5,6 @@ import android.content.Intent import android.net.VpnService import android.os.ParcelFileDescriptor import android.support.v4.content.LocalBroadcastManager -import android.util.Log import java8.util.concurrent.CompletableFuture import org.apache.commons.configuration2.ex.ConversionException import org.bouncycastle.openssl.PEMException @@ -22,19 +21,28 @@ import org.pacien.tincapp.extensions.Java.applyIgnoringException import org.pacien.tincapp.extensions.VpnServiceBuilder.applyCfg import org.pacien.tincapp.intent.Actions import org.pacien.tincapp.utils.TincKeyring +import org.slf4j.Logger +import org.slf4j.LoggerFactory import java.io.FileNotFoundException /** * @author pacien */ class TincVpnService : VpnService() { + private var logger: Logger? = null + + override fun onCreate() { + super.onCreate() + logger = LoggerFactory.getLogger(this.javaClass) + } + override fun onDestroy() { stopVpn() super.onDestroy() } override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { - Log.i(TAG, intent.action) + logger?.info("Intent received: {}", intent.action) when { intent.action == Actions.ACTION_CONNECT && intent.scheme == Actions.TINC_SCHEME -> @@ -61,7 +69,7 @@ class TincVpnService : VpnService() { if (!AppPaths.confDir(netName).exists()) return reportError(resources.getString(R.string.message_no_configuration_for_network_format, netName), docTopic = "configuration") - Log.i(TAG, "Starting tinc daemon for network \"$netName\".") + logger?.info("Starting tinc daemon for network \"$netName\".") if (isConnected()) stopVpn() val interfaceCfg = try { @@ -108,17 +116,17 @@ class TincVpnService : VpnService() { if (exception != null) { reportError(resources.getString(R.string.message_daemon_exited, exception.cause!!.message!!), exception) } else { - Log.i(TAG, "tinc daemon started.") + logger?.info("tinc daemon started.") broadcastEvent(Actions.EVENT_CONNECTED) } } } private fun stopVpn(): Unit = synchronized(this) { - Log.i(TAG, "Stopping any running tinc daemon.") + logger?.info("Stopping any running tinc daemon.") netName?.let { Tinc.stop(it).thenRun { - Log.i(TAG, "All tinc daemons stopped.") + logger?.info("All tinc daemons stopped.") broadcastEvent(Actions.EVENT_DISCONNECTED) setState(null, null, null, null) } @@ -127,9 +135,9 @@ class TincVpnService : VpnService() { private fun reportError(msg: String, e: Throwable? = null, docTopic: String? = null) { if (e != null) - Log.e(TAG, msg, e) + logger?.error(msg, e) else - Log.e(TAG, msg) + logger?.error(msg) broadcastEvent(Actions.EVENT_ABORTED) App.alert(R.string.title_unable_to_start_tinc, msg, @@ -147,7 +155,6 @@ class TincVpnService : VpnService() { companion object { private const val SETUP_DELAY = 500L // ms - private val TAG = this::class.java.canonicalName!! private var netName: String? = null private var interfaceCfg: VpnInterfaceConfiguration? = null private var fd: ParcelFileDescriptor? = null -- cgit v1.2.3