Updated on 2026-08-14
This commit is contained in:
parent
842b3e494a
commit
e01127186c
14 changed files with 128 additions and 131 deletions
|
|
@ -1,69 +1,109 @@
|
|||
package com.tangem.datasource.local.logs
|
||||
|
||||
import androidx.datastore.preferences.core.MutablePreferences
|
||||
import com.tangem.datasource.local.preferences.AppPreferencesStore
|
||||
import com.tangem.datasource.local.preferences.PreferencesKeys
|
||||
import android.content.Context
|
||||
import com.tangem.utils.coroutines.CoroutineDispatcherProvider
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import org.joda.time.DateTime
|
||||
import org.joda.time.format.DateTimeFormatterBuilder
|
||||
import timber.log.Timber
|
||||
import java.io.BufferedWriter
|
||||
import java.io.File
|
||||
import java.io.FileWriter
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* Store for saving app logs
|
||||
*
|
||||
* @property appPreferencesStore app preferences store
|
||||
* @param dispatchers coroutine dispatcher provider
|
||||
* @property applicationContext application context
|
||||
* @param dispatchers coroutine dispatcher provider
|
||||
*
|
||||
[REDACTED_AUTHOR]
|
||||
*/
|
||||
@Singleton
|
||||
class AppLogsStore @Inject constructor(
|
||||
private val appPreferencesStore: AppPreferencesStore,
|
||||
@ApplicationContext private val applicationContext: Context,
|
||||
dispatchers: CoroutineDispatcherProvider,
|
||||
) {
|
||||
|
||||
private val scope = CoroutineScope(dispatchers.io)
|
||||
private val mutex = Mutex()
|
||||
|
||||
private val file = File(applicationContext.filesDir, LOG_FILE_NAME)
|
||||
|
||||
private val formatter = DateTimeFormatterBuilder()
|
||||
.appendDayOfMonth(2)
|
||||
.appendLiteral('.')
|
||||
.appendMonthOfYear(2)
|
||||
.appendLiteral(' ')
|
||||
.appendHourOfDay(1)
|
||||
.appendLiteral(':')
|
||||
.appendMinuteOfHour(2)
|
||||
.appendLiteral(':')
|
||||
.appendSecondOfMinute(2)
|
||||
.appendLiteral('.')
|
||||
.appendMillisOfSecond(3)
|
||||
.toFormatter()
|
||||
|
||||
/** Get log file */
|
||||
fun getFile(): File? = if (file.exists()) file else null
|
||||
|
||||
/** Save log [message] */
|
||||
fun saveLogMessage(message: String) {
|
||||
val newLogs = DateTime.now().millis.toString() to message
|
||||
launchWithLock {
|
||||
createFileIfNotExist()
|
||||
|
||||
appPreferencesStore.editDataWithLock { preferences ->
|
||||
val savedLogs = preferences.getObjectMap<String>(PreferencesKeys.APP_LOGS_KEY)
|
||||
writeMessage(message)
|
||||
}
|
||||
}
|
||||
|
||||
preferences.setObjectMap(key = PreferencesKeys.APP_LOGS_KEY, value = savedLogs + newLogs)
|
||||
/** Save log that consists from [messages] */
|
||||
fun saveLogMessage(vararg messages: String) {
|
||||
launchWithLock {
|
||||
createFileIfNotExist()
|
||||
|
||||
writeMessage(*messages)
|
||||
}
|
||||
}
|
||||
|
||||
/** Delete deprecated logs if file size exceeds [maxSize] */
|
||||
fun deleteDeprecatedLogs(maxSize: Int) {
|
||||
appPreferencesStore.editDataWithLock { preferences ->
|
||||
val savedLogs = preferences.getObjectMap<String>(PreferencesKeys.APP_LOGS_KEY)
|
||||
|
||||
var sum = 0
|
||||
preferences.setObjectMap(
|
||||
key = PreferencesKeys.APP_LOGS_KEY,
|
||||
value = savedLogs.entries
|
||||
.sortedBy(Map.Entry<String, String>::key)
|
||||
.takeLastWhile {
|
||||
sum += it.value.length
|
||||
sum < maxSize
|
||||
}
|
||||
.associate { it.key to it.value },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun AppPreferencesStore.editDataWithLock(
|
||||
transform: suspend AppPreferencesStore.(MutablePreferences) -> Unit,
|
||||
) {
|
||||
scope.launch {
|
||||
mutex.withLock {
|
||||
editData(transform)
|
||||
launchWithLock {
|
||||
if (file.exists() && file.length() > maxSize) {
|
||||
file.delete()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeMessage(vararg messages: String) {
|
||||
BufferedWriter(FileWriter(file, true)).use { writer ->
|
||||
writer.append(formatter.print(DateTime.now()))
|
||||
writer.append(": ")
|
||||
messages.forEach(writer::append)
|
||||
writer.newLine()
|
||||
}
|
||||
}
|
||||
|
||||
private fun createFileIfNotExist() {
|
||||
if (!file.exists()) {
|
||||
runCatching { file.createNewFile() }
|
||||
.onFailure(Timber::e)
|
||||
}
|
||||
}
|
||||
|
||||
private fun launchWithLock(callback: () -> Unit) {
|
||||
scope.launch {
|
||||
mutex.withLock {
|
||||
callback()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val LOG_FILE_NAME = "logs.txt"
|
||||
}
|
||||
}
|
||||
|
|
@ -54,8 +54,8 @@ internal class NetworkLogsSaveInterceptor(
|
|||
val connectionProtocol = if (connection != null) " ${connection.protocol()}" else ""
|
||||
|
||||
appLogsStore.saveLogMessage(
|
||||
"--> ${request.method} ${request.url}$connectionProtocol\n" +
|
||||
createRequestEndMessage(request),
|
||||
"--> ${request.method} ${request.url}$connectionProtocol\n",
|
||||
createRequestEndMessage(request),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -88,12 +88,6 @@ internal class NetworkLogsSaveInterceptor(
|
|||
}
|
||||
|
||||
private fun logResponseMessage(response: Response, startNs: Long) {
|
||||
val tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs)
|
||||
|
||||
val responseMessage = if (response.message.isEmpty()) "" else ' ' + response.message
|
||||
val startMessage = "<-- ${response.code}$responseMessage ${response.request.url} " +
|
||||
"(${tookMs}ms)"
|
||||
|
||||
val responseHeaders = response.headers
|
||||
val responseBody = response.body!!
|
||||
val contentLength = responseBody.contentLength()
|
||||
|
|
@ -138,7 +132,17 @@ internal class NetworkLogsSaveInterceptor(
|
|||
}
|
||||
}
|
||||
|
||||
appLogsStore.saveLogMessage(startMessage + "\n" + message)
|
||||
val tookMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs)
|
||||
|
||||
val spaceBeforeResponseMessage = if (response.message.isEmpty()) "" else ' ' + response.message
|
||||
|
||||
appLogsStore.saveLogMessage(
|
||||
"<-- ${response.code}",
|
||||
spaceBeforeResponseMessage,
|
||||
response.message,
|
||||
" ${response.request.url} (${tookMs}ms)\n",
|
||||
message,
|
||||
)
|
||||
}
|
||||
|
||||
private fun bodyHasUnknownEncoding(headers: Headers): Boolean {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue