Updated on 2026-08-14

This commit is contained in:
Tangem 2024-08-22 14:57:15 +04:00
parent 842b3e494a
commit e01127186c
14 changed files with 128 additions and 131 deletions

View file

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

View file

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