From c028b9b5b6b0e8cba4e6a47f99cfa8fdca26eba5 Mon Sep 17 00:00:00 2001 From: Zane Schepke Date: Tue, 9 Dec 2025 10:45:34 -0500 Subject: [PATCH] fix: logger start and clear --- .../WireGuardAutoTunnel.kt | 12 +++- .../viewmodel/LoggerViewModel.kt | 63 ++++++++----------- .../viewmodel/SharedAppViewModel.kt | 8 ++- .../com/zaneschepke/logcatter/LogReader.kt | 4 +- .../zaneschepke/logcatter/LogcatManager.kt | 48 +++++++++----- .../logcatter/LogcatStreamReader.kt | 8 +-- 6 files changed, 78 insertions(+), 65 deletions(-) diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/WireGuardAutoTunnel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/WireGuardAutoTunnel.kt index 1f3ed4d2..e33d7a86 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/WireGuardAutoTunnel.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/WireGuardAutoTunnel.kt @@ -18,6 +18,7 @@ import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.distinctUntilChangedBy import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import timber.log.Timber @@ -59,8 +60,15 @@ class WireGuardAutoTunnel : Application(), Configuration.Provider { applicationScope.launch(ioDispatcher) { launch { - val monitoringSettings = monitoringRepository.getMonitoringSettings() - if (monitoringSettings.isLocalLogsEnabled) logReader.start() + monitoringRepository.flow + .distinctUntilChangedBy { it.isLocalLogsEnabled } + .collect { settings -> + if (settings.isLocalLogsEnabled) { + logReader.start() + } else { + logReader.stop() + } + } } launch { notificationMonitor.handleApplicationNotifications() } } diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/viewmodel/LoggerViewModel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/viewmodel/LoggerViewModel.kt index b8aa99b5..c0220f0e 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/viewmodel/LoggerViewModel.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/viewmodel/LoggerViewModel.kt @@ -6,7 +6,6 @@ import com.zaneschepke.logcatter.LogReader import com.zaneschepke.wireguardautotunnel.BuildConfig import com.zaneschepke.wireguardautotunnel.R import com.zaneschepke.wireguardautotunnel.domain.repository.GlobalEffectRepository -import com.zaneschepke.wireguardautotunnel.domain.repository.MonitoringSettingsRepository import com.zaneschepke.wireguardautotunnel.domain.sideeffect.GlobalSideEffect import com.zaneschepke.wireguardautotunnel.ui.state.LoggerUiState import com.zaneschepke.wireguardautotunnel.util.Constants @@ -15,8 +14,6 @@ import com.zaneschepke.wireguardautotunnel.util.StringValue import dagger.hilt.android.lifecycle.HiltViewModel import javax.inject.Inject import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.* import org.orbitmvi.orbit.ContainerHost import org.orbitmvi.orbit.viewmodel.container import timber.log.Timber @@ -26,7 +23,6 @@ class LoggerViewModel @Inject constructor( private val logReader: LogReader, - private val monitoringRepository: MonitoringSettingsRepository, private val fileUtils: FileUtils, private val globalEffectRepository: GlobalEffectRepository, ) : ContainerHost, ViewModel() { @@ -38,33 +34,17 @@ constructor( buildSettings = { repeatOnSubscribedStopTimeout = 5000L }, ) { intent { - monitoringRepository.flow - .onEach { reduce { state.copy(monitoringSettings = it) } } - .distinctUntilChangedBy { it.isLocalLogsEnabled } - .onEach { settings -> - if (settings.isLocalLogsEnabled) { - logReader.start() - } else { - logReader.stop() - logReader.deleteAndClearLogs() - reduce { state.copy(messages = emptyList()) } - } - } - .flatMapLatest { settings -> - if (settings.isLocalLogsEnabled) logReader.bufferedLogs else emptyFlow() - } - .catch { e -> Timber.e(e) } - .collect { logMessage -> - reduce { - state.copy( - messages = - state.messages.toMutableList().apply { - if (size >= MAX_LOG_SIZE) removeAt(0) - add(logMessage) - } - ) - } + logReader.bufferedLogs.collect { logMessage -> + reduce { + state.copy( + messages = + state.messages.toMutableList().apply { + if (size >= MAX_LOG_SIZE) removeAt(0) + add(logMessage) + } + ) } + } } } @@ -91,17 +71,24 @@ constructor( } Unit } - result.onSuccess { file -> - logReader.zipLogFiles(file.absolutePath) - fileUtils.exportFile(file, uri, FileUtils.ZIP_FILE_MIME_TYPE).onFailure(onFailure) - } - result.onFailure(onFailure) + result.fold( + onSuccess = { file -> + try { + logReader.zipLogFiles(file.absolutePath) + fileUtils + .exportFile(file, uri, FileUtils.ZIP_FILE_MIME_TYPE) + .onFailure(onFailure) + } finally { + if (file.exists()) file.delete() + } + }, + onFailure = onFailure, + ) } fun deleteLogs() = intent { - monitoringRepository.upsert(state.monitoringSettings.copy(isLocalLogsEnabled = false)) - delay(1_000L) - monitoringRepository.upsert(state.monitoringSettings.copy(isLocalLogsEnabled = true)) + reduce { state.copy(messages = emptyList()) } + logReader.deleteAndClearLogs() } companion object { diff --git a/app/src/main/java/com/zaneschepke/wireguardautotunnel/viewmodel/SharedAppViewModel.kt b/app/src/main/java/com/zaneschepke/wireguardautotunnel/viewmodel/SharedAppViewModel.kt index bfe4c9fb..414cc9f5 100644 --- a/app/src/main/java/com/zaneschepke/wireguardautotunnel/viewmodel/SharedAppViewModel.kt +++ b/app/src/main/java/com/zaneschepke/wireguardautotunnel/viewmodel/SharedAppViewModel.kt @@ -320,8 +320,12 @@ constructor( fileUtils .createNewShareFile(shareFileName) .onSuccess { - fileUtils.zipAll(it, files).onFailure(onFailure) - fileUtils.exportFile(it, uri, FileUtils.ZIP_FILE_MIME_TYPE).onFailure(onFailure) + try { + fileUtils.zipAll(it, files).onFailure(onFailure) + fileUtils.exportFile(it, uri, FileUtils.ZIP_FILE_MIME_TYPE).onFailure(onFailure) + } finally { + if (it.exists()) it.delete() + } postSideEffect( GlobalSideEffect.Snackbar(StringValue.StringResource(R.string.export_success)) ) diff --git a/logcatter/src/main/java/com/zaneschepke/logcatter/LogReader.kt b/logcatter/src/main/java/com/zaneschepke/logcatter/LogReader.kt index e6bd7c54..db78153d 100644 --- a/logcatter/src/main/java/com/zaneschepke/logcatter/LogReader.kt +++ b/logcatter/src/main/java/com/zaneschepke/logcatter/LogReader.kt @@ -4,9 +4,9 @@ import com.zaneschepke.logcatter.model.LogMessage import kotlinx.coroutines.flow.Flow interface LogReader { - fun start() + suspend fun start() - fun stop() + suspend fun stop() suspend fun zipLogFiles(path: String) diff --git a/logcatter/src/main/java/com/zaneschepke/logcatter/LogcatManager.kt b/logcatter/src/main/java/com/zaneschepke/logcatter/LogcatManager.kt index 13b4112c..ac645d2f 100644 --- a/logcatter/src/main/java/com/zaneschepke/logcatter/LogcatManager.kt +++ b/logcatter/src/main/java/com/zaneschepke/logcatter/LogcatManager.kt @@ -8,6 +8,8 @@ import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.asSharedFlow +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock class LogcatManager(pid: Int, logDir: String, maxFileSize: Long, maxFolderSize: Long) : LogReader, DefaultLifecycleObserver { @@ -17,6 +19,8 @@ class LogcatManager(pid: Int, logDir: String, maxFileSize: Long, maxFolderSize: private var logJob: Job? = null private var isStarted = false + private val mutex = Mutex() + private val _bufferedLogs = MutableSharedFlow( replay = 10_000, @@ -30,39 +34,49 @@ class LogcatManager(pid: Int, logDir: String, maxFileSize: Long, maxFolderSize: override fun onCreate(owner: LifecycleOwner) { // for auto start - // start() + // logScope.launch { start() } } override fun onDestroy(owner: LifecycleOwner) { - stop() + logScope.launch { stop() } logScope.cancel() } - override fun start() { - if (isStarted) return - stop() - logJob = - logScope.launch { - logcatReader.readLogs().collect { logMessage -> - _bufferedLogs.emit(logMessage) - _liveLogs.emit(logMessage) + override suspend fun start() { + mutex.withLock { + if (isStarted) return + stopInternal() + logJob = + logScope.launch { + logcatReader.readLogs().collect { logMessage -> + _bufferedLogs.emit(logMessage) + _liveLogs.emit(logMessage) + } } - } - isStarted = true + isStarted = true + } } @OptIn(ExperimentalCoroutinesApi::class) - override fun stop() { - if (!isStarted) return + override suspend fun stop() { + mutex.withLock { + if (!isStarted) return + stopInternal() + isStarted = false + } + } + + @OptIn(ExperimentalCoroutinesApi::class) + private fun stopInternal() { logJob?.cancel() logcatReader.stop() fileManager.close() _bufferedLogs.resetReplayCache() - isStarted = false + logJob = null } override suspend fun zipLogFiles(path: String) { - val wasStarted = isStarted + val wasStarted = mutex.withLock { isStarted } stop() fileManager.zipLogs(path) if (wasStarted) { @@ -73,7 +87,7 @@ class LogcatManager(pid: Int, logDir: String, maxFileSize: Long, maxFolderSize: @OptIn(ExperimentalCoroutinesApi::class) override suspend fun deleteAndClearLogs() { - val wasStarted = isStarted + val wasStarted = mutex.withLock { isStarted } stop() _bufferedLogs.resetReplayCache() fileManager.deleteAllLogs() diff --git a/logcatter/src/main/java/com/zaneschepke/logcatter/LogcatStreamReader.kt b/logcatter/src/main/java/com/zaneschepke/logcatter/LogcatStreamReader.kt index 9324376f..0b9e4db1 100644 --- a/logcatter/src/main/java/com/zaneschepke/logcatter/LogcatStreamReader.kt +++ b/logcatter/src/main/java/com/zaneschepke/logcatter/LogcatStreamReader.kt @@ -9,11 +9,11 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flowOn -class LogcatStreamReader(private val pid: Int, private val fileManager: LogFileManager) { +class LogcatStreamReader(pid: Int, private val fileManager: LogFileManager) { private val bufferSize = 1024 private var process: Process? = null private var reader: BufferedReader? = null - private val command = "logcat -v epoch | grep \"($pid)\"" + private val command = "logcat -v epoch --pid=$pid" private val clearCommand = "logcat -c" private val ioDispatcher = Dispatchers.IO @@ -30,7 +30,7 @@ class LogcatStreamReader(private val pid: Int, private val fileManager: LogFileM emit(LogMessage.from(line)) } } - } catch (e: IOException) { + } catch (_: IOException) { // do nothing } finally { stop() @@ -43,7 +43,7 @@ class LogcatStreamReader(private val pid: Int, private val fileManager: LogFileM try { process = Runtime.getRuntime().exec(command) reader = BufferedReader(InputStreamReader(process!!.inputStream), bufferSize) - } catch (e: IOException) { + } catch (_: IOException) { // do nothing } }