diff --git a/core/subsonic-api/build.gradle b/core/subsonic-api/build.gradle index 2e4582fe..a95c4078 100644 --- a/core/subsonic-api/build.gradle +++ b/core/subsonic-api/build.gradle @@ -10,6 +10,7 @@ dependencies { } implementation other.kotlinReflect // for jackson kotlin, but to use the same version implementation other.okhttpLogging + implementation other.timber testImplementation testing.kotlinJunit testImplementation testing.mockito diff --git a/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClient.kt b/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClient.kt index 0d7e097e..6b32b07c 100644 --- a/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClient.kt +++ b/core/subsonic-api/src/main/kotlin/org/moire/ultrasonic/api/subsonic/SubsonicAPIClient.kt @@ -36,6 +36,7 @@ private const val READ_TIMEOUT = 60_000L */ class SubsonicAPIClient( config: SubsonicClientConfiguration, + private val okLogger: HttpLoggingInterceptor.Logger = HttpLoggingInterceptor.Logger.DEFAULT, baseOkClient: OkHttpClient = OkHttpClient.Builder().build() ) { private val versionInterceptor = VersionInterceptor(config.minimalProtocolVersion) @@ -176,7 +177,7 @@ class SubsonicAPIClient( } private fun OkHttpClient.Builder.addLogging() { - val loggingInterceptor = HttpLoggingInterceptor() + val loggingInterceptor = HttpLoggingInterceptor(okLogger) loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY this.addInterceptor(loggingInterceptor) } diff --git a/ultrasonic/build.gradle b/ultrasonic/build.gradle index 9676252f..d74e9589 100644 --- a/ultrasonic/build.gradle +++ b/ultrasonic/build.gradle @@ -75,6 +75,7 @@ dependencies { implementation other.kotlinxCoroutines implementation other.koinAndroid implementation other.koinViewModel + implementation other.okhttpLogging kapt androidSupport.room diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/activity/SubsonicTabActivity.java b/ultrasonic/src/main/java/org/moire/ultrasonic/activity/SubsonicTabActivity.java index c26a8a88..fd2a9651 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/activity/SubsonicTabActivity.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/activity/SubsonicTabActivity.java @@ -1232,11 +1232,14 @@ public class SubsonicTabActivity extends ResultActivity implements OnClickListen { file = new File(FileUtil.getUltrasonicDirectory(context), filename); printWriter = new PrintWriter(file); - printWriter.println("Android API level: " + Build.VERSION.SDK_INT); - printWriter.println("Ultrasonic version name: " + Util.getVersionName(context)); - printWriter.println("Ultrasonic version code: " + Util.getVersionCode(context)); - printWriter.println(); + + String logMessage = String.format( + "Android API level: %s\nUltrasonic version name: %s\nUltrasonic version code: %s\n\n", + Build.VERSION.SDK_INT, Util.getVersionName(context), Util.getVersionCode(context)); + + printWriter.println(logMessage); throwable.printStackTrace(printWriter); + Timber.e(throwable, "Uncaught Exception! %s", logMessage); Timber.i("Stack trace written to %s", file); } catch (Throwable x) diff --git a/ultrasonic/src/main/java/org/moire/ultrasonic/util/FileUtil.java b/ultrasonic/src/main/java/org/moire/ultrasonic/util/FileUtil.java index f1bd5828..972a0ff4 100644 --- a/ultrasonic/src/main/java/org/moire/ultrasonic/util/FileUtil.java +++ b/ultrasonic/src/main/java/org/moire/ultrasonic/util/FileUtil.java @@ -188,7 +188,7 @@ public class FileUtil Timber.e(ex, "Exception in BitmapFactory.decodeFile()"); } - Timber.i("getAvatarBitmap %i", String.valueOf(size)); + Timber.i("getAvatarBitmap %s", String.valueOf(size)); if (bitmap != null) { @@ -258,7 +258,7 @@ public class FileUtil Timber.e(ex, "Exception in BitmapFactory.decodeFile()"); } - Timber.i("getAlbumArtBitmap %i", String.valueOf(size)); + Timber.i("getAlbumArtBitmap %s", String.valueOf(size)); if (bitmap != null) { @@ -294,7 +294,7 @@ public class FileUtil opt.inJustDecodeBounds = false; } - Timber.i("getSampledBitmap %i", String.valueOf(size)); + Timber.i("getSampledBitmap %s", String.valueOf(size)); return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, opt); } diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/app/UApp.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/app/UApp.kt index 946d6e6d..210b209e 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/app/UApp.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/app/UApp.kt @@ -12,6 +12,9 @@ import org.moire.ultrasonic.di.directoriesModule import org.moire.ultrasonic.di.featureFlagsModule import org.moire.ultrasonic.di.mediaPlayerModule import org.moire.ultrasonic.di.musicServiceModule +import org.moire.ultrasonic.log.FileLoggerTree +import org.moire.ultrasonic.log.TimberKoinLogger +import org.moire.ultrasonic.log.timberLogger import timber.log.Timber import timber.log.Timber.DebugTree @@ -21,12 +24,12 @@ class UApp : MultiDexApplication() { if (BuildConfig.DEBUG) { Timber.plant(DebugTree()) + Timber.plant(FileLoggerTree(this)) } startKoin { - // Use Koin Android Logger // TODO Current version of Koin has a bug, which forces the usage of Level.ERROR - androidLogger(Level.ERROR) + timberLogger(Level.ERROR) // declare Android context androidContext(this@UApp) // declare modules to use diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MusicServiceModule.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MusicServiceModule.kt index f522925d..bca16832 100644 --- a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MusicServiceModule.kt +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/di/MusicServiceModule.kt @@ -1,6 +1,7 @@ @file:JvmName("MusicServiceModule") package org.moire.ultrasonic.di +import okhttp3.logging.HttpLoggingInterceptor import kotlin.math.abs import org.koin.android.ext.koin.androidContext import org.koin.core.qualifier.named @@ -11,6 +12,7 @@ import org.moire.ultrasonic.api.subsonic.SubsonicAPIVersions import org.moire.ultrasonic.api.subsonic.SubsonicClientConfiguration import org.moire.ultrasonic.cache.PermanentFileStorage import org.moire.ultrasonic.data.ActiveServerProvider +import org.moire.ultrasonic.log.TimberOkHttpLogger import org.moire.ultrasonic.service.CachedMusicService import org.moire.ultrasonic.service.MusicService import org.moire.ultrasonic.service.OfflineMusicService @@ -54,7 +56,8 @@ val musicServiceModule = module { ) } - single { SubsonicAPIClient(get()) } + single { TimberOkHttpLogger() } + single { SubsonicAPIClient(get(), get()) } single(named(ONLINE_MUSIC_SERVICE)) { CachedMusicService(RESTMusicService(get(), get())) diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/log/FileLoggerTree.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/log/FileLoggerTree.kt new file mode 100644 index 00000000..3ef42191 --- /dev/null +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/log/FileLoggerTree.kt @@ -0,0 +1,44 @@ +package org.moire.ultrasonic.log + +import android.content.Context +import org.moire.ultrasonic.util.FileUtil +import org.moire.ultrasonic.util.Util +import timber.log.Timber +import java.io.File +import java.io.FileWriter + +/** + * A Timber Tree which can be used to log to a file + * Subclass of the DebugTree so it inherits the Tag handling + */ +class FileLoggerTree(val context: Context) : Timber.DebugTree() { + private val filename = "ultrasonic.log" + + override fun log(priority: Int, tag: String?, message: String, t: Throwable?) { + var file: File? = null + var writer: FileWriter? = null + try { + file = File(FileUtil.getUltrasonicDirectory(context), filename) + writer = FileWriter(file, true) + val exceptionString = t?.toString() ?: ""; + writer.write("${logLevelToString(priority)} $tag $message $exceptionString\n") + writer.flush() + } catch (x: Throwable) { + super.e(x, "Failed to write log to %s", file) + } finally { + if (writer != null) Util.close(writer) + } + } + + private fun logLevelToString(logLevel: Int) : String { + return when(logLevel) { + 2 -> "V" + 3 -> "D" + 4 -> "I" + 5 -> "W" + 6 -> "E" + 7 -> "A" + else -> "U" + } + } +} \ No newline at end of file diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/log/TimberKoinLogger.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/log/TimberKoinLogger.kt new file mode 100644 index 00000000..060e6bfb --- /dev/null +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/log/TimberKoinLogger.kt @@ -0,0 +1,38 @@ +package org.moire.ultrasonic.log + +import org.koin.core.KoinApplication +import org.koin.core.logger.Level +import org.koin.core.logger.Logger +import org.koin.core.logger.MESSAGE +import timber.log.Timber + +/** + * KoinApplication Extension to use Timber with Koin + */ +fun KoinApplication.timberLogger( + level: Level = Level.INFO +): KoinApplication { + koin._logger = TimberKoinLogger(level) + return this +} + +/** + * Timber Logger implementation for Koin + */ +class TimberKoinLogger (level: Level = Level.INFO) : Logger(level) { + + override fun log(level: Level, msg: MESSAGE) { + if (this.level <= level) { + logOnLevel(msg, level) + } + } + + private fun logOnLevel(msg: MESSAGE, level: Level) { + when (level) { + Level.DEBUG -> Timber.d(msg) + Level.INFO -> Timber.i(msg) + Level.ERROR -> Timber.e(msg) + else -> Timber.e(msg) + } + } +} diff --git a/ultrasonic/src/main/kotlin/org/moire/ultrasonic/log/TimberOkHttpLogger.kt b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/log/TimberOkHttpLogger.kt new file mode 100644 index 00000000..e2073a73 --- /dev/null +++ b/ultrasonic/src/main/kotlin/org/moire/ultrasonic/log/TimberOkHttpLogger.kt @@ -0,0 +1,13 @@ +package org.moire.ultrasonic.log + +import okhttp3.logging.HttpLoggingInterceptor +import timber.log.Timber + +/** + * Timber Logging implementation for HttpLoggingInterceptor + */ +class TimberOkHttpLogger : HttpLoggingInterceptor.Logger { + override fun log(message: String) { + Timber.d(message) + } +} \ No newline at end of file