Improve sync logging

This commit is contained in:
Koitharu
2023-05-17 17:00:42 +03:00
parent 51ff1ff7b7
commit 43a92bdf08
6 changed files with 63 additions and 18 deletions

View File

@@ -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) {
}
}

View File

@@ -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}")
}
}
}

View File

@@ -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
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}

View File

@@ -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()
}
}