fix: Add modified version of RollingFileLogWriter

This commit is contained in:
Ahmad Ansori Palembani 2024-11-22 19:02:38 +07:00
parent 5fa5815541
commit d7160db53a
Signed by: null2264
GPG key ID: BA64F8B60AF3EFB6
5 changed files with 175 additions and 50 deletions

View file

@ -56,21 +56,19 @@ import eu.kanade.tachiyomi.util.system.GLUtil
import eu.kanade.tachiyomi.util.system.ImageUtil
import eu.kanade.tachiyomi.util.system.launchIO
import eu.kanade.tachiyomi.util.system.localeContext
import eu.kanade.tachiyomi.util.system.nameWithoutExtension
import eu.kanade.tachiyomi.util.system.notification
import eu.kanade.tachiyomi.util.system.setToDefault
import eu.kanade.tachiyomi.util.system.setupFileLog
import java.security.Security
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.io.files.Path
import org.conscrypt.Conscrypt
import org.koin.core.context.startKoin
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import uy.kohesive.injekt.injectLazy
import yokai.core.CrashlyticsLogWriter
import yokai.core.RollingUniFileLogWriter
import yokai.core.di.appModule
import yokai.core.di.domainModule
import yokai.core.di.preferenceModule
@ -113,7 +111,7 @@ open class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.F
val scope = ProcessLifecycleOwner.get().lifecycleScope
Logger.setToDefault(buildLogWritersToAdd(storageManager.getLogsFile()))
Logger.setToDefault(buildLogWritersToAdd(storageManager.getLogsDirectory()))
basePreferences.crashReport().changes()
.onEach {
@ -284,18 +282,11 @@ open class App : Application(), DefaultLifecycleObserver, SingletonImageLoader.F
}
fun buildLogWritersToAdd(
logFile: UniFile?,
logPath: UniFile?,
) = buildList {
if (!BuildConfig.DEBUG) add(CrashlyticsLogWriter())
//val fileName = logFile?.nameWithoutExtension
//val filePath = logFile?.parentFile?.filePath?.let { path -> Path(path) }
//if (filePath != null && fileName != null) add(
// Logger.setupFileLog(
// logFileName = fileName,
// logPath = filePath,
// )
//)
if (logPath != null) add(RollingUniFileLogWriter(logPath))
}
private const val ACTION_DISABLE_INCOGNITO_MODE = "tachi.action.DISABLE_INCOGNITO_MODE"

View file

@ -0,0 +1,160 @@
package yokai.core
import co.touchlab.kermit.DefaultFormatter
import co.touchlab.kermit.LogWriter
import co.touchlab.kermit.Message
import co.touchlab.kermit.MessageStringFormatter
import co.touchlab.kermit.Severity
import co.touchlab.kermit.Tag
import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.util.system.launchIO
import eu.kanade.tachiyomi.util.system.withIOContext
import java.io.IOException
import java.text.DateFormat
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.trySendBlocking
import kotlinx.coroutines.currentCoroutineContext
import kotlinx.coroutines.isActive
import kotlinx.coroutines.newSingleThreadContext
/**
* Copyright (c) 2024 Touchlab
* SPDX-License-Identifier: Apache-2.0
*
* Kermit's RollingFileLogWriter, modified to use UniFile since using KotlinX IO's FileSystem keep throwing
* "Permission Denied". Also added try-catch, in case "Permission Denied" is back to haunt me
*
* REF: https://github.com/touchlab/Kermit/blob/c9af0b7d3344b430f4ed2668e74d02f34ba1905a/kermit-io/src/commonMain/kotlin/co/touchlab/kermit/io/RollingFileLogWriter.kt
*/
class RollingUniFileLogWriter(
private val logPath: UniFile,
private val rollOnSize: Long = 10 * 1024 * 1024, // 10MB
private val maxLogFiles: Int = 5,
private val messageStringFormatter: MessageStringFormatter = DefaultFormatter,
private val messageDateFormat: DateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS", Locale.getDefault())
) : LogWriter() {
@OptIn(DelicateCoroutinesApi::class, ExperimentalCoroutinesApi::class)
private val coroutineScope = CoroutineScope(
newSingleThreadContext("RollingUniFileLogWriter") +
SupervisorJob() +
CoroutineName("RollingUniFileLogWriter") +
CoroutineExceptionHandler { _, throwable ->
println("RollingUniFileLogWriter: Uncaught exception in writer coroutine")
throwable.printStackTrace()
}
)
private val loggingChannel: Channel<ByteArray> = Channel()
init {
coroutineScope.launchIO {
writer()
}
}
override fun log(severity: Severity, message: String, tag: String, throwable: Throwable?) {
bufferLog(
formatMessage(
severity = severity,
tag = Tag(tag),
message = Message(message),
), throwable
)
}
private fun bufferLog(message: String, throwable: Throwable?) {
val log = buildString {
append(messageDateFormat.format(Date()))
append(" ")
appendLine(message)
if (throwable != null) {
appendLine(throwable.stackTraceToString())
}
}
loggingChannel.trySendBlocking(log.toByteArray())
}
private fun formatMessage(severity: Severity, tag: Tag?, message: Message): String =
messageStringFormatter.formatMessage(severity, tag, message)
private fun maybeRollLogs(size: Long): Boolean {
return if (size > rollOnSize) {
rollLogs()
true
} else false
}
private fun rollLogs() {
if (pathForLogIndex(maxLogFiles - 1)?.exists() == true) {
pathForLogIndex(maxLogFiles - 1)?.delete()
}
(0..<(maxLogFiles - 1)).reversed().forEach {
val sourcePath = pathForLogIndex(it)
val targetFileName = fileNameForLogIndex(it + 1)
if (sourcePath?.exists() == true) {
try {
sourcePath.renameTo(targetFileName)
} catch (e: Exception) {
println("RollingUniFileLogWriter: Failed to roll log file ${sourcePath.filePath} to $targetFileName (sourcePath exists=${sourcePath.exists()})")
e.printStackTrace()
}
}
}
}
private fun fileNameForLogIndex(index: Int): String {
val date = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date())
return if (index == 0) "${date}-${BuildConfig.BUILD_TYPE}.log" else "${date}-${BuildConfig.BUILD_TYPE} (${index}).log"
}
private fun pathForLogIndex(index: Int, create: Boolean = false): UniFile? {
return if (create) logPath.createFile(fileNameForLogIndex(index)) else logPath.findFile(fileNameForLogIndex(index))
}
private suspend fun writer() = withIOContext {
val logFilePath = pathForLogIndex(0)
if (logFilePath?.exists() == true) {
maybeRollLogs(fileSize(logFilePath))
}
fun openNewOutput() = pathForLogIndex(0, true)?.openOutputStream(true)
var currentLogSink = openNewOutput()
while (currentCoroutineContext().isActive) {
val result = loggingChannel.receiveCatching()
val rolled = maybeRollLogs(fileSize(logFilePath))
if (rolled) {
currentLogSink?.close()
currentLogSink = openNewOutput()
}
result.getOrNull()?.let {
try {
currentLogSink?.write(it)
} catch (e: IOException) {
// Probably "Permission Denied" is back to haunt me
println("RollingUniFileLogWriter: Failed to write to log file")
e.printStackTrace()
}
}
currentLogSink?.flush()
}
}
private fun fileSize(path: UniFile?) = path?.length() ?: -1L
}

View file

@ -4,13 +4,9 @@ import android.content.Context
import androidx.core.net.toUri
import co.touchlab.kermit.Logger
import com.hippo.unifile.UniFile
import eu.kanade.tachiyomi.BuildConfig
import eu.kanade.tachiyomi.buildLogWritersToAdd
import eu.kanade.tachiyomi.util.storage.DiskUtil
import eu.kanade.tachiyomi.util.system.setToDefault
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.channels.Channel
@ -21,7 +17,6 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.shareIn
import kotlinx.io.files.Path
class StorageManager(
private val context: Context,
@ -50,15 +45,15 @@ class StorageManager(
}
parent.createDirectory(COVERS_PATH)
parent.createDirectory(PAGES_PATH)
parent.createDirectory(LOGS_PATH)
}
try {
Logger.setToDefault(buildLogWritersToAdd(getLogsFile()))
} catch (e: Exception) {
// Just in case something went horribly wrong
Logger.setToDefault(buildLogWritersToAdd(null))
Logger.e(e) { "Something went wrong while trying to setup log file" }
parent.createDirectory(LOGS_PATH)?.also {
try {
Logger.setToDefault(buildLogWritersToAdd(it))
} catch (e: Exception) {
// Just in case something went horribly wrong
Logger.setToDefault(buildLogWritersToAdd(null))
Logger.e(e) { "Something went wrong while trying to setup log file" }
}
}
}
_changes.send(Unit)
@ -98,11 +93,6 @@ class StorageManager(
fun getLogsDirectory(): UniFile? {
return baseDir?.createDirectory(LOGS_PATH)
}
fun getLogsFile(): UniFile? {
val date = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()).format(Date())
return getLogsDirectory()?.createFile("${date}-${BuildConfig.BUILD_TYPE}.log")
}
}
private const val BACKUPS_PATH = "backup"

View file

@ -2,27 +2,12 @@ package eu.kanade.tachiyomi.util.system
import co.touchlab.kermit.LogWriter
import co.touchlab.kermit.Logger
import co.touchlab.kermit.io.RollingFileLogWriter
import co.touchlab.kermit.io.RollingFileLogWriterConfig
import co.touchlab.kermit.platformLogWriter
import kotlinx.io.files.Path
fun Logger.w(e: Throwable) = w(e) { "Something is not right..." }
fun Logger.e(e: Throwable) = e(e) { "Something went wrong!" }
fun Logger.setToDefault(
writersToAdd: List<LogWriter>,
) {
fun Logger.setToDefault(writersToAdd: List<LogWriter>) {
Logger.setLogWriters(listOf(platformLogWriter()) + writersToAdd)
Logger.setTag("Yokai")
}
fun Logger.setupFileLog(logFileName: String, logPath: Path): LogWriter {
return RollingFileLogWriter(
config = RollingFileLogWriterConfig(
logFileName = logFileName,
logFilePath = logPath,
maxLogFiles = 1,
)
)
}

View file

@ -44,7 +44,6 @@ flexible-adapter = { module = "com.github.arkon.FlexibleAdapter:flexible-adapter
image-decoder = { module = "com.github.tachiyomiorg:image-decoder", version = "41c059e540" }
kermit = { module = "co.touchlab:kermit", version.ref = "kermit" }
kermit-io = { module = "co.touchlab:kermit-io", version.ref = "kermit" }
koin-bom = { module = "io.insert-koin:koin-bom", version.ref = "koin" }
koin-core = { module = "io.insert-koin:koin-core" }
@ -114,7 +113,7 @@ sqldelight = { id = "app.cash.sqldelight", version.ref = "sqldelight" }
db = [ "sqldelight-coroutines" ]
db-android = [ "sqldelight-android-driver", "sqldelight-android-paging" ]
coil = [ "coil3", "coil3-svg", "coil3-gif", "coil3-okhttp" ]
logging = [ "kermit", "kermit-io" ]
logging = [ "kermit" ]
# FIXME: Uncomment once SQLDelight support KMP AndroidX SQLiteDriver
#sqlite = [ "sqlite", "sqlite-ktx" ]
sqlite = [ "sqlite-ktx" ]