Improve sync logging
This commit is contained in:
@@ -4,12 +4,15 @@ import android.content.Context
|
||||
import androidx.annotation.WorkerThread
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.cancelAndJoin
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.runInterruptible
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.utils.ext.printStackTraceDebug
|
||||
import org.koitharu.kotatsu.utils.ext.processLifecycleScope
|
||||
@@ -82,6 +85,15 @@ class FileLogger(
|
||||
flushImpl()
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun flushBlocking() {
|
||||
if (!isEnabled) {
|
||||
return
|
||||
}
|
||||
runBlockingSafe { flushJob?.cancelAndJoin() }
|
||||
runBlockingSafe { flushImpl() }
|
||||
}
|
||||
|
||||
private fun postFlush() {
|
||||
if (flushJob?.isActive == true) {
|
||||
return
|
||||
@@ -96,10 +108,10 @@ class FileLogger(
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun flushImpl() {
|
||||
private suspend fun flushImpl() = withContext(NonCancellable) {
|
||||
mutex.withLock {
|
||||
if (buffer.isEmpty()) {
|
||||
return
|
||||
return@withContext
|
||||
}
|
||||
runInterruptible(Dispatchers.IO) {
|
||||
if (file.length() > MAX_SIZE_BYTES) {
|
||||
@@ -131,4 +143,9 @@ class FileLogger(
|
||||
}
|
||||
bakFile.delete()
|
||||
}
|
||||
|
||||
private inline fun runBlockingSafe(crossinline block: suspend () -> Unit) = try {
|
||||
runBlocking(NonCancellable) { block() }
|
||||
} catch (_: InterruptedException) {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import androidx.annotation.WorkerThread
|
||||
import androidx.core.content.contentValuesOf
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import org.koitharu.kotatsu.R
|
||||
@@ -22,7 +23,9 @@ import org.koitharu.kotatsu.core.db.TABLE_HISTORY
|
||||
import org.koitharu.kotatsu.core.db.TABLE_MANGA
|
||||
import org.koitharu.kotatsu.core.db.TABLE_MANGA_TAGS
|
||||
import org.koitharu.kotatsu.core.db.TABLE_TAGS
|
||||
import org.koitharu.kotatsu.core.logs.LoggersModule
|
||||
import org.koitharu.kotatsu.core.network.GZipInterceptor
|
||||
import org.koitharu.kotatsu.core.prefs.AppSettings
|
||||
import org.koitharu.kotatsu.parsers.util.json.mapJSONTo
|
||||
import org.koitharu.kotatsu.sync.data.SyncAuthApi
|
||||
import org.koitharu.kotatsu.sync.data.SyncAuthenticator
|
||||
@@ -61,6 +64,7 @@ class SyncHelper(
|
||||
}
|
||||
private val defaultGcPeriod: Long // gc period if sync enabled
|
||||
get() = TimeUnit.DAYS.toMillis(4)
|
||||
private val logger = LoggersModule.provideSyncLogger(context, AppSettings(context))
|
||||
|
||||
fun syncFavourites(syncResult: SyncResult) {
|
||||
val data = JSONObject()
|
||||
@@ -71,7 +75,7 @@ class SyncHelper(
|
||||
.url("$baseUrl/resource/$TABLE_FAVOURITES")
|
||||
.post(data.toRequestBody())
|
||||
.build()
|
||||
val response = httpClient.newCall(request).execute().parseJsonOrNull()
|
||||
val response = httpClient.newCall(request).execute().log().parseJsonOrNull()
|
||||
if (response != null) {
|
||||
val timestamp = response.getLong(FIELD_TIMESTAMP)
|
||||
val categoriesResult =
|
||||
@@ -93,7 +97,7 @@ class SyncHelper(
|
||||
.url("$baseUrl/resource/$TABLE_HISTORY")
|
||||
.post(data.toRequestBody())
|
||||
.build()
|
||||
val response = httpClient.newCall(request).execute().parseJsonOrNull()
|
||||
val response = httpClient.newCall(request).execute().log().parseJsonOrNull()
|
||||
if (response != null) {
|
||||
val result = upsertHistory(
|
||||
json = response.getJSONArray(TABLE_HISTORY),
|
||||
@@ -105,6 +109,19 @@ class SyncHelper(
|
||||
gcHistory()
|
||||
}
|
||||
|
||||
fun onError(e: Throwable) {
|
||||
if (logger.isEnabled) {
|
||||
logger.log("Sync error", e)
|
||||
}
|
||||
}
|
||||
|
||||
fun onSyncComplete(result: SyncResult) {
|
||||
if (logger.isEnabled) {
|
||||
logger.log("Sync finshed: ${result.toDebugString()}")
|
||||
logger.flushBlocking()
|
||||
}
|
||||
}
|
||||
|
||||
private fun upsertHistory(json: JSONArray, timestamp: Long): Array<ContentProviderResult> {
|
||||
val uri = uri(authorityHistory, TABLE_HISTORY)
|
||||
val operations = ArrayList<ContentProviderOperation>()
|
||||
@@ -298,4 +315,10 @@ class SyncHelper(
|
||||
private fun JSONObject.removeJSONObject(name: String) = remove(name) as JSONObject
|
||||
|
||||
private fun JSONObject.removeJSONArray(name: String) = remove(name) as JSONArray
|
||||
|
||||
private fun Response.log() = apply {
|
||||
if (logger.isEnabled) {
|
||||
logger.log("$code ${request.url}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,8 +14,6 @@ import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.EntryPointAccessors
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import org.koitharu.kotatsu.core.db.*
|
||||
import org.koitharu.kotatsu.core.logs.FileLogger
|
||||
import org.koitharu.kotatsu.core.logs.SyncLogger
|
||||
import java.util.concurrent.Callable
|
||||
|
||||
abstract class SyncProvider : ContentProvider() {
|
||||
@@ -24,7 +22,6 @@ abstract class SyncProvider : ContentProvider() {
|
||||
EntryPointAccessors.fromApplication(checkNotNull(context), SyncProviderEntryPoint::class.java)
|
||||
}
|
||||
private val database by lazy { entryPoint.database }
|
||||
private val logger by lazy { entryPoint.logger }
|
||||
|
||||
private val supportedTables = setOf(
|
||||
TABLE_FAVOURITES,
|
||||
@@ -52,7 +49,6 @@ abstract class SyncProvider : ContentProvider() {
|
||||
.selection(selection, selectionArgs)
|
||||
.orderBy(sortOrder)
|
||||
.create()
|
||||
logger.log("query: ${sqlQuery.sql} (${selectionArgs.contentToString()})")
|
||||
return database.openHelper.readableDatabase.query(sqlQuery)
|
||||
}
|
||||
|
||||
@@ -65,7 +61,6 @@ abstract class SyncProvider : ContentProvider() {
|
||||
if (values == null || table == null) {
|
||||
return null
|
||||
}
|
||||
logger.log { "insert: $table [$values]" }
|
||||
val db = database.openHelper.writableDatabase
|
||||
if (db.insert(table, SQLiteDatabase.CONFLICT_IGNORE, values) < 0) {
|
||||
db.update(table, values)
|
||||
@@ -75,7 +70,6 @@ abstract class SyncProvider : ContentProvider() {
|
||||
|
||||
override fun delete(uri: Uri, selection: String?, selectionArgs: Array<out String>?): Int {
|
||||
val table = getTableName(uri) ?: return 0
|
||||
logger.log { "delete: $table ($selection) : (${selectionArgs.contentToString()})" }
|
||||
return database.openHelper.writableDatabase.delete(table, selection, selectionArgs)
|
||||
}
|
||||
|
||||
@@ -84,7 +78,6 @@ abstract class SyncProvider : ContentProvider() {
|
||||
if (values == null || table == null) {
|
||||
return 0
|
||||
}
|
||||
logger.log { "update: $table ($selection) : (${selectionArgs.contentToString()}) [$values]" }
|
||||
return database.openHelper.writableDatabase
|
||||
.update(table, SQLiteDatabase.CONFLICT_IGNORE, values, selection, selectionArgs)
|
||||
}
|
||||
@@ -127,8 +120,5 @@ abstract class SyncProvider : ContentProvider() {
|
||||
interface SyncProviderEntryPoint {
|
||||
|
||||
val database: MangaDatabase
|
||||
|
||||
@get:SyncLogger
|
||||
val logger: FileLogger
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,10 @@ class FavouritesSyncAdapter(context: Context) : AbstractThreadedSyncAdapter(cont
|
||||
runCatchingCancellable {
|
||||
syncHelper.syncFavourites(syncResult)
|
||||
SyncController.setLastSync(context, account, authority, System.currentTimeMillis())
|
||||
}.onFailure(syncResult::onError)
|
||||
}.onFailure { e ->
|
||||
syncResult.onError(e)
|
||||
syncHelper.onError(e)
|
||||
}
|
||||
syncHelper.onSyncComplete(syncResult)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,10 @@ class HistorySyncAdapter(context: Context) : AbstractThreadedSyncAdapter(context
|
||||
runCatchingCancellable {
|
||||
syncHelper.syncHistory(syncResult)
|
||||
SyncController.setLastSync(context, account, authority, System.currentTimeMillis())
|
||||
}.onFailure(syncResult::onError)
|
||||
}.onFailure { e ->
|
||||
syncResult.onError(e)
|
||||
syncHelper.onError(e)
|
||||
}
|
||||
syncHelper.onSyncComplete(syncResult)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.koitharu.kotatsu.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.widget.Toast
|
||||
import androidx.core.app.ShareCompat
|
||||
import androidx.core.content.FileProvider
|
||||
import org.koitharu.kotatsu.BuildConfig
|
||||
@@ -84,6 +85,7 @@ class ShareHelper(private val context: Context) {
|
||||
fun shareLogs(loggers: Collection<FileLogger>) {
|
||||
val intentBuilder = ShareCompat.IntentBuilder(context)
|
||||
.setType(TYPE_TEXT)
|
||||
var hasLogs = false
|
||||
for (logger in loggers) {
|
||||
val logFile = logger.file
|
||||
if (!logFile.exists()) {
|
||||
@@ -91,8 +93,13 @@ class ShareHelper(private val context: Context) {
|
||||
}
|
||||
val uri = FileProvider.getUriForFile(context, "${BuildConfig.APPLICATION_ID}.files", logFile)
|
||||
intentBuilder.addStream(uri)
|
||||
hasLogs = true
|
||||
}
|
||||
if (hasLogs) {
|
||||
intentBuilder.setChooserTitle(R.string.share_logs)
|
||||
intentBuilder.startChooser()
|
||||
} else {
|
||||
Toast.makeText(context, R.string.nothing_here, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
intentBuilder.setChooserTitle(R.string.share_logs)
|
||||
intentBuilder.startChooser()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user