Skip to content

Instantly share code, notes, and snippets.

@megaacheyounes
Created October 31, 2023 12:24
Show Gist options
  • Save megaacheyounes/e1121e5275818492310e8dcab012737f to your computer and use it in GitHub Desktop.
Save megaacheyounes/e1121e5275818492310e8dcab012737f to your computer and use it in GitHub Desktop.
Tree for Timber Android logger with persistent storage to txt file
package com.megaache.myapp.util
import android.os.Environment
import android.util.Log
import com.megaache.myapp.BuildConfig
import com.megaache.myapp.FmtApp
import timber.log.Timber
import timber.log.Timber.DebugTree
import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.io.IOException
import java.math.BigDecimal
import java.math.RoundingMode
/**
* Created by YounesMegaacheY84154336 on 9/9/2020.
*/
class VSLogTree : DebugTree() {
init {
try {
val p = File(getDir())
if (!p.exists())
p.mkdirs()
Log.println(Log.DEBUG, "timber", "log dir created => ${p.absolutePath}")
} catch (e: Exception) {
if (BuildConfig.DEBUG)
throw e
Log.println(Log.ERROR, "timber", " $e")
}
}
override fun createStackElementTag(element: StackTraceElement): String {
element.apply {
val methodFirst10 = methodName.substring(
0,
if (methodName.length > 10)
10
else
methodName.length
)
return String.format(
"Timber(%s:%s)#%s",
element.fileName,
element.lineNumber,
methodFirst10
)
}
}
override fun log(priority: Int, tag: String?, message: String, t: Throwable?) {
if (BuildConfig.DEBUG)
super.log(
priority,
tag,
"[${timeSinceStarted()}s] ${formatMessage(priority, message)}",
t
)
try {
val file = File(getPath())
if (!file.exists()) {
val success = file.createNewFile()
Log.d("timber", "is log file created => $success")
}
val log = "->[${timeSinceStarted()}s] $tag ${formatMessage(priority, message)}"
//SharedPrefHelper.saveLog(log)
FileOutputStream(file, true).bufferedWriter().use { writer ->
writer.appendLine(log)
writer.flush()
}
} catch (e: IOException) {
//app may not have permission to write log file, so ignore it
}
}
private fun formatMessage(priority: Int, msg: String) = when (priority) {
Log.ERROR -> """${"\n"}
|*********************************************UNCAUGHT EXCEPTION:START***************************************************
|*$msg
|*********************************************UNCAUGHT EXCEPTION:END*****************************************************"""
.trimMargin()
Log.WARN -> """${"\n"}
|********************************************* WARNING ***************************************************
|*$msg"""
.trimMargin()
else -> msg
}
private fun timeSinceStarted() =
BigDecimal((System.currentTimeMillis() - START_TIME.toDouble()) / 1000).setScale(
4,
RoundingMode.HALF_EVEN
)
companion object {
val START_TIME = System.currentTimeMillis()
//clear logs afte 1 day
const val LOG_PURGE_DAYS = 1
const val TAG = "MyApp"
//current time millis is 13 digits, we only keep the last 3 days logs,
//mean the start time for logs will be different in the last 9 digits (day), the first 5 digits will always be equal (ex: month year)
//so we remove to static part keep the name short
//this is valid until Nov 20th 2286, we have enough time
private var DAY_MILLIS = START_TIME.toString().substring(5) //pick only last 9 digits
private fun getName() =
"$TAG${BuildConfig.VERSION_NAME}(${BuildConfig.BUILD_TYPE})_$DAY_MILLIS.txt"
private fun getDir() =
"${FmtApp.instance.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)}${File.separator }${TAG}_Logs"
fun getPath() = getDir() + File.separator + getName()
fun getLogs() =
FileInputStream(File(getPath())).bufferedReader().use { reader ->
reader.readLines().toList()
}
//delete log files older than 24 hours
fun cleanOldLogs() = try {
var deleted = 0
Timber.d("CLEANING log files older than ${LOG_PURGE_DAYS * 24} hours...")
val directory = File(getDir())
if (directory.exists()) {
val listFiles = directory.listFiles()
if (listFiles == null) {
Timber.d("0 logs files found")
} else {
Timber.d("Found ${listFiles.size} log files")
val purgeTime = System.currentTimeMillis() - LOG_PURGE_DAYS * 24 * 60 * 60 * 1000
for (listFile in listFiles) {
if (listFile.lastModified() < purgeTime) {
if (listFile.delete()) {
deleted++
} else {
Timber.w("Unable to delete log file: $listFile")
}
}
}
}
Timber.d("cleaned $deleted log files")
} else {
Timber.w("logs dir does not exist")
}
true
} catch (e: Exception) {
if (BuildConfig.DEBUG) throw e
Timber.w("$e")
false
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment