Created
June 23, 2022 08:03
-
-
Save cohenItay/05d3f8b41c8ecd25bbecf8e4b1b30e73 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class DiskLoggerImpl( | |
private val appContext: Context, | |
private val logsFileProvider: LogsFileProvider | |
) : DiskLogger { | |
private val TAG = DiskLogger::class.simpleName!! | |
private val timeFormat = SimpleDateFormat("HH:mm:ss.SSS", Locale.getDefault()) | |
private var bufferWriter: BufferedWriter? = null | |
private val loggerScope = CoroutineScope(Dispatchers.Default + Job()) | |
private val channel = Channel<Operation>() | |
private var closeBufferJob: Job? = null | |
init { | |
loggerScope.launch { | |
for (op in channel) { | |
when (op) { | |
is Operation.Write -> | |
processWriteLog(op.logContent) | |
is Operation.Delete -> | |
processCleanLogs(op.from) | |
} | |
} | |
} | |
} | |
@Suppress("BlockingMethodInNonBlockingContext") // It is inside withContext(Dispatchers.IO), probably a bug in the IDE | |
override fun log(logContent: String) { | |
loggerScope.launch { | |
// Working with the channel makes the communications between the coroutines sequential | |
// and thus works like a queue | |
channel.send(Operation.Write(logContent)) | |
} | |
} | |
override fun cleanLogs(from: Date) { | |
val diff = TimeUnit.MILLISECONDS.toDays(Calendar.getInstance().timeInMillis - from.time) | |
if (diff < 1L) | |
error("Less then one day time difference") | |
loggerScope.launch { | |
channel.send(Operation.Delete(from)) | |
} | |
} | |
private suspend fun processCleanLogs(from: Date) = withContext(Dispatchers.IO) { | |
if (Calendar.getInstance().time.time <= from.time) | |
return@withContext | |
val directory = logsFileProvider.provideLogsDirectory(appContext) | |
if (!directory.exists()) | |
return@withContext | |
val allInnerFiles = FileUtils.getAllFilesInDir(directory) | |
val filesToDelete = allInnerFiles.filter { file -> | |
val lastModified = file.lastModified() | |
if (lastModified <= 0L) { | |
false | |
} else { | |
Date(lastModified).before(from) | |
} | |
} | |
filesToDelete.forEach { file -> | |
if (!file.delete()) { | |
Log.e(TAG, "processCleanLogs: Was not able to delete file ${file.name}") | |
} | |
} | |
} | |
/** | |
* Process the log, which is write it to the disk. | |
*/ | |
private suspend fun processWriteLog(logContent: String) = withContext(Dispatchers.IO) { | |
val writer = validateWriterIsReady() ?: return@withContext | |
val time = timeFormat.format(Calendar.getInstance().time) | |
val log = "$time $logContent\n" | |
try { | |
@Suppress("BlockingMethodInNonBlockingContext") // lint bug, it is inside IO Context | |
writer.write(log) | |
} catch (e: IOException) { | |
PLogger.e(TAG, "couldn't write the log: '$log'", e) | |
} | |
closeBufferJob?.cancelAndJoin() | |
closeBufferJob = createCloseBufferJob() | |
} | |
@Suppress( "BlockingMethodInNonBlockingContext") // This lint is shown for withContext while it should not, bug. | |
private suspend fun validateWriterIsReady() : BufferedWriter? = | |
bufferWriter ?: withContext(Dispatchers.IO) { | |
val file = logsFileProvider.provideFile(appContext) | |
?: return@withContext null | |
try { | |
bufferWriter = BufferedWriter(FileWriter(file, true)) | |
} catch (e: IOException) { | |
PLogger.e(TAG, "$file, is a directory rather then a file", e) | |
bufferWriter?.close() | |
} | |
bufferWriter!! | |
} | |
private fun createCloseBufferJob() = loggerScope.launch(Dispatchers.IO) { | |
delay(2 * 60 * 1_000L) // 2 minutes | |
if (isActive) { | |
bufferWriter?.close() | |
bufferWriter = null | |
} | |
} | |
private sealed class Operation { | |
data class Write(val logContent: String) : Operation() | |
data class Delete(val from: Date) : Operation() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment